UrForth: some microoptimisations
[urasm.git] / src / urasm-old.c
blobf701fec408c91f4ca8023a61171c53425d2c60d6
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 "liburforth/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 static 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 += (*s++);
82 hash += hash<<10;
83 hash ^= hash>>6;
85 // finalize
86 hash += hash<<3;
87 hash ^= hash>>11;
88 hash += hash<<15;
89 return hash;
93 //==========================================================================
95 // joaatHashStrCI
97 //==========================================================================
98 static uint32_t joaatHashStrCI (const void *buf) {
99 uint32_t hash = 0x29a;
100 const uint8_t *s = (const uint8_t *)buf;
101 size_t len = (s != NULL ? strlen((const char *)s) : 0);
102 while (len--) {
103 hash += (*s++)|0x20; // this converts ASCII capitals to locase (and destroys other, but who cares)
104 hash += hash<<10;
105 hash ^= hash>>6;
107 // finalize
108 hash += hash<<3;
109 hash ^= hash>>11;
110 hash += hash<<15;
111 return hash;
115 ////////////////////////////////////////////////////////////////////////////////
116 UR_FORCE_INLINE int isSpace (char ch) { return (ch && ((unsigned)(ch&0xff) <= 32 || ch == 127)); }
117 UR_FORCE_INLINE int isAlpha (char ch) { return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')); }
118 UR_FORCE_INLINE int isDigit (char ch) { return (ch >= '0' && ch <= '9'); }
119 UR_FORCE_INLINE int isHexDigit (char ch) { return ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')); }
120 UR_FORCE_INLINE int isAlphaDigit (char ch) { return (isAlpha(ch) || isDigit(ch)); }
122 UR_FORCE_INLINE char toUpper (char ch) { return (ch >= 'a' && ch <= 'z' ? ch-'a'+'A' : ch); }
123 UR_FORCE_INLINE char toLower (char ch) { return (ch >= 'A' && ch <= 'Z' ? ch-'A'+'a' : ch); }
125 static int digitInBase (char ch, int base) {
126 if (ch < '0') return -1;
127 if (base <= 10) {
128 if (ch >= '0'+base) return -1;
129 return ch-'0';
131 ch = toUpper(ch);
132 if (ch <= '9') return ch-'0';
133 if (ch < 'A' || ch > 'A'+base-10) return -1;
134 ch -= 'A'-10;
135 return (ch < base ? ch : -1);
139 //==========================================================================
141 // strEquCI
143 //==========================================================================
144 static int strEquCI (const char *s0, const char *s1) {
145 int res = 1;
146 while (res && *s0 && *s1) {
147 char c0 = *s0++; if (c0 >= 'A' && c0 <= 'Z') c0 = c0 - 'A' + 'a';
148 char c1 = *s1++; if (c1 >= 'A' && c1 <= 'Z') c1 = c1 - 'A' + 'a';
149 res = (c0 == c1);
151 return (res && s0[0] == 0 && s1[0] == 0);
155 ////////////////////////////////////////////////////////////////////////////////
156 static char *strprintfVA (const char *fmt, va_list vaorig) {
157 char *buf = NULL;
158 int olen, len = 128;
160 buf = malloc(len);
161 if (buf == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
162 for (;;) {
163 char *nb;
164 va_list va;
166 va_copy(va, vaorig);
167 olen = vsnprintf(buf, len, fmt, va);
168 va_end(va);
169 if (olen >= 0 && olen < len) return buf;
170 if (olen < 0) olen = len*2-1;
171 nb = realloc(buf, olen+1);
172 if (nb == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
173 buf = nb;
174 len = olen+1;
179 static __attribute__((format(printf,1,2))) char *strprintf (const char *fmt, ...) {
180 char *buf = NULL;
181 va_list va;
183 va_start(va, fmt);
184 buf = strprintfVA(fmt, va);
185 va_end(va);
186 return buf;
190 ///////////////////////////////////////////////////////////////////////////////
191 // global variables
193 enum {
194 AMODE_NORMAL = 0,
195 AMODE_UFO = 1, // scanning UrForth code
198 static int asmMode = AMODE_NORMAL;
200 static char *ufoIncludeDir = NULL;
201 static char *sysIncludeDir = NULL;
202 static char *refFileName = NULL;
203 static char *lastIncludePath = NULL;
204 static char *lastSysIncludePath = NULL;
205 static int urasm_dump_all_labels = 1; // 0: do not write all-uppercase labels
207 #define MAX_LINE_SIZE (1024*1024)
208 static char currLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
210 // set by `processLabel()`
211 static char currSeenLabel[1024];
213 static uint32_t ufoMacroVocId = 0;
216 ///////////////////////////////////////////////////////////////////////////////
217 // init dirs
219 static void initInclideDir (void) {
220 const char *id = getenv("URASM_INCLUDE_DIR");
221 if (id && id[0]) {
222 sysIncludeDir = strdup(id);
223 } else {
224 char myDir[4096];
225 memset(myDir, 0, sizeof(myDir));
226 #ifndef WIN32
227 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
228 strcpy(myDir, ".");
229 } else {
230 char *p = (char *)strrchr(myDir, '/');
231 if (!p) strcpy(myDir, "."); else *p = '\0';
233 #else
234 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
235 char *p = strrchr(myDir, '\\');
236 if (!p) strcpy(myDir, "."); else *p = '\0';
237 #endif
238 strcat(myDir, "/libs");
239 sysIncludeDir = strdup(myDir);
241 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
242 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
246 static void initUFEInclideDir (void) {
247 const char *id = getenv("URASM_URFORTH_INCLUDE_DIR");
248 if (id && id[0]) {
249 ufoIncludeDir = strdup(id);
250 } else {
251 char myDir[4096];
252 memset(myDir, 0, sizeof(myDir));
253 #ifndef WIN32
254 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
255 strcpy(myDir, ".");
256 } else {
257 char *p = (char *)strrchr(myDir, '/');
258 if (!p) strcpy(myDir, "."); else *p = '\0';
260 #else
261 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
262 char *p = strrchr(myDir, '\\');
263 if (!p) strcpy(myDir, "."); else *p = '\0';
264 #endif
265 strcat(myDir, "/urflibs");
266 ufoIncludeDir = strdup(myDir);
268 while (ufoIncludeDir[0] && ufoIncludeDir[strlen(ufoIncludeDir)-1] == '/') ufoIncludeDir[strlen(ufoIncludeDir)-1] = '\0';
269 if (!ufoIncludeDir[0]) strcpy(ufoIncludeDir, ".");
273 ///////////////////////////////////////////////////////////////////////////////
274 // string utilities
276 /* trim trailing spaces and comments; normalize colons */
277 static void normalizeStr (char *s) {
278 if (asmMode == AMODE_UFO) return; // do nothing
279 char *p = s;
280 /* now skip all shit */
281 while (*p) {
282 const char ch = *p++;
283 /* check for "af'" */
284 if (ch == '\'' && p-s >= 2 && toLower(p[-2] == 'a') && toLower(p[-1] == 'f')) continue;
285 /* comment */
286 if (ch == ';') { p[-1] = 0; break; }
287 /* string */
288 if (ch == '"' || ch == '\'') {
289 const char qch = ch;
290 while (*p) {
291 const char c1 = *p++;
292 if (c1 == qch) break;
293 if (c1 == '\\' && *p) ++p;
295 continue;
297 /* reduce and normalise colons */
298 if (ch == ':') {
299 --p; /* back to colon */
300 /* remove spaces before colon */
301 char *t = p;
302 while (t != s && isSpace(t[-1])) --t;
303 if (t != p) memmove(t, p, strlen(p)+1);
304 p = t;
305 ur_assert(p[0] == ':');
306 ++p; /* skip colon */
307 /* remove following spaces and colons */
308 t = p;
309 while (*t == ':' || isSpace(*t)) ++t;
310 if (t != p) memmove(p, t, strlen(t)+1);
311 continue;
314 /* done; trim trailing spaces and colons */
315 size_t slen = strlen(s);
316 while (slen > 0 && (isSpace(s[slen-1]) || s[slen-1] == ':')) --slen;
317 s[slen] = 0;
321 /* check if string starts with the given command (case-insensitive) */
322 /* returns NULL or pointer to args */
323 /* skips spaces after command if any */
324 static char *strIsCommand (const char *command, char *str) {
325 for (int cnt = 1; cnt > 0; --cnt) {
326 while (*str && isSpace(*str)) ++str; // skip spaces
327 if (*str == '$') ++str;
328 for (; *command && *str; ++command, ++str) {
329 if (toUpper(*command) != toUpper(*str)) return NULL; // alas
331 if (*command) return NULL; // alas
332 if (*str && isAlphaDigit(*str)) return NULL; // alas
333 while (*str && isSpace(*str)) ++str; // skip spaces
334 if (asmMode != AMODE_UFO) {
335 if (*str && *str == ':') break; // try again if we have a colon
337 return str; // found
339 return NULL;
343 /* parse string literal */
344 /* don't free() result */
345 /* skips trailing spaces */
346 static char *parseStr (char **str, char endQ, int *lenp) {
347 static char buf[MAX_LINE_SIZE];
348 int len = 0, n, f, base;
349 char *a = *str;
350 memset(buf, 0, sizeof(buf));
351 if (lenp) *lenp = 0;
352 for (; *a; ++a) {
353 if (*a == '\\') {
354 if (!a[1]) break;
355 switch (*(++a)) {
356 case 'a': buf[len++] = '\a'; break;
357 case 'b': buf[len++] = '\b'; break;
358 case 'e': buf[len++] = '\x1b'; break;
359 case 'f': buf[len++] = '\f'; break;
360 case 'n': buf[len++] = '\n'; break;
361 case 'r': buf[len++] = '\r'; break;
362 case 't': buf[len++] = '\t'; break;
363 case 'v': buf[len++] = '\v'; break;
364 case 'z': buf[len++] = '\0'; break;
365 case 'x': case 'X': // hex
366 ++a; // skip 'x'
367 base = 16; f = 2;
368 donum: for (n = 0; f > 0; --f) {
369 char ch = digitInBase(*a++, base);
371 if (ch < 0) { --a; break; }
372 n *= base;
373 n += ch;
375 buf[len++] = n;
376 --a; // return to the last digit, 'for' will skip it
377 break;
378 case '0': // octal
379 base = 8; f = 4;
380 goto donum;
381 case '1' ... '9': // decimal
382 base = 10; f = 3;
383 goto donum;
384 default: buf[len++] = a[0]; break; // others
386 } else {
387 if (*a == endQ) { ++a; break; }
388 buf[len++] = *a;
391 while (*a && isSpace(*a)) ++a; // skip trailing spaces
392 *str = a;
393 buf[len] = '\0';
394 if (lenp) *lenp = len;
395 return buf;
399 ///////////////////////////////////////////////////////////////////////////////
400 // source file stack, reader, etc
402 typedef struct SourceLine {
403 struct SourceLine *next;
404 char *line;
405 const char *fname;
406 int lineNo;
407 int system;
408 } SourceLine;
410 static SourceLine *asmText = NULL;
411 static SourceLine *currSrcLine = NULL;
413 typedef struct FileInfo_s {
414 struct FileInfo_s *next;
415 char *name;
416 } FileInfo;
418 static FileInfo *knownFiles = NULL;
421 static void clearKnownFiles (void) {
422 while (knownFiles) {
423 FileInfo *fi = knownFiles;
424 knownFiles = fi->next;
425 if (fi->name) free(fi->name);
426 free(fi);
431 static const char *appendKnownFileName (const char *fn) {
432 if (!fn || !fn[0]) return "<unknown>";
433 for (FileInfo *fi = knownFiles; fi; fi = fi->next) {
434 if (strcmp(fi->name, fn) == 0) return fi->name;
436 FileInfo *fi = calloc(1, sizeof(FileInfo));
437 ur_assert((fi->name = strdup(fn)) != NULL);
438 fi->next = knownFiles;
439 knownFiles = fi;
440 return fi->name;
444 // ////////////////////////////////////////////////////////////////////////// //
445 #define MAX_MACRO_ARGS (32)
447 typedef struct MacroDef {
448 struct MacroDef *next;
449 char *name;
450 SourceLine *lines;
451 int argc;
452 char *argdefaults[MAX_MACRO_ARGS]; // default values
453 char *argnames[MAX_MACRO_ARGS]; // argument names
454 } MacroDef;
456 typedef struct {
457 MacroDef *mac;
458 char *argvals[MAX_MACRO_ARGS]; // argument values
459 } CurMacroDef;
461 static MacroDef *maclist = NULL;
462 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
463 static int curmacronum = 0;
466 static void freeLineList (SourceLine *list) {
467 while (list) {
468 SourceLine *l = list;
469 list = l->next;
470 free(l->line);
471 //free(l->fname);
472 free(l);
477 static void clearMacros (void) {
478 while (maclist) {
479 MacroDef *md = maclist;
480 maclist = md->next;
481 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) {
482 if (md->argdefaults[f]) free(md->argdefaults[f]);
483 if (md->argnames[f]) free(md->argnames[f]);
485 free(md->name);
486 freeLineList(md->lines);
487 free(md);
492 static MacroDef *findMacro (const char *name) {
493 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strEquCI(name, mc->name)) return mc;
494 return NULL;
498 static void asmTextClear (void) {
499 freeLineList(asmText);
500 asmText = NULL;
501 currSrcLine = NULL;
505 #define currLineTrimBlanks() do { \
506 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
507 currLine[slen] = 0; \
508 } while (0)
510 #define currLineRemoveComment() do { \
511 size_t pos = 0; \
512 char instr = 0; \
513 while (pos < slen) { \
514 if (instr) { \
515 if (currLine[pos] == '\\' && pos+1 < slen) { \
516 ++pos; \
517 } else if (currLine[pos] == instr) { \
518 instr = 0; \
520 } else { \
521 if (currLine[pos] == ';') { slen = pos; break; } \
522 if (currLine[pos] == '"') { \
523 instr = '"'; \
524 } else if (currLine[pos] == '\'') { \
525 if (pos == 0 || \
526 (currLine[pos-1] != '_' && !isAlphaDigit(currLine[pos-1]))) \
528 instr = currLine[pos]; \
532 ++pos; \
534 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
535 currLine[slen] = 0; \
536 } while (0)
539 static SourceLine *loadTextFile (FILE *fl, int system, const char *fname, int *error) {
540 if (error) *error = 0;
542 fname = appendKnownFileName(fname);
543 int continuation = 0;
545 SourceLine *first = NULL;
546 SourceLine *last = NULL;
547 int lineNo = 0;
549 // read file
550 while (fgets(currLine, sizeof(currLine)-1, fl)) {
551 ++lineNo;
552 currLine[sizeof(currLine)-1] = '\0';
553 size_t slen = strlen(currLine);
555 if (slen == 0 || (currLine[slen-1] != '\n' && currLine[slen-1] != '\r')) {
556 if (!feof(fl)) {
557 if (error) *error = 1;
558 fprintf(stderr, "ERROR: file %s, line %d: line too long\n", fname, lineNo);
559 freeLineList(first);
560 return NULL;
564 currLineTrimBlanks();
566 int new_cont = 0;
567 if (slen && currLine[slen-1] == '\\') {
568 // this is a possible continuation line
569 if (slen > 1 && currLine[slen-2] == '\\') {
570 // nope, fix it
571 slen -= 1;
572 currLine[slen] = 0;
573 } else {
574 new_cont = 1;
575 --slen;
576 currLineTrimBlanks();
580 //!normalizeStr(currLine);
581 if (!currLine[0]) {
582 // don't store empty lines
583 continuation = new_cont;
584 continue;
587 // remove comments from continuations
588 if (continuation || new_cont) {
589 currLineRemoveComment();
592 // continuation?
593 if (continuation) {
594 // join continuation with the last line
595 ur_assert(last);
596 size_t newlen = slen+strlen(last->line)+4;
597 if (newlen > MAX_LINE_SIZE-8) {
598 if (error) *error = 1;
599 fprintf(stderr, "ERROR: too long continuation at line %d in file '%s'\n", lineNo, fname);
600 freeLineList(first);
601 return NULL;
603 char *newbuf = malloc(newlen);
604 ur_assert(newbuf); // who cares
605 snprintf(newbuf, newlen, "%s%s", last->line, currLine);
606 free(last->line);
607 last->line = newbuf;
608 } else {
609 // add current line
610 SourceLine *s = calloc(1, sizeof(SourceLine));
611 ur_assert(s);
612 s->system = system;
613 s->lineNo = lineNo;
614 ur_assert((s->line = strdup(currLine)) != NULL);
615 s->fname = fname;
616 if (last) last->next = s; else first = s;
617 last = s;
620 continuation = new_cont;
623 return first;
627 static int asmTextLoad (const char *fname, int system) {
628 FILE *fl;
629 int error;
631 if (!(fl = fopen(fname, "r"))) {
632 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
633 return -1;
635 printf("loading: %s\n", fname);
637 SourceLine *first = loadTextFile(fl, 0, fname, &error);
638 fclose(fl);
640 if (error) return -1;
642 SourceLine *last = asmText;
643 if (last) {
644 while (last->next) last = last->next;
645 last->next = first;
646 } else {
647 asmText = first;
649 return 0;
653 static char *extractFileDir (const char *s) {
654 if (!s || !s[0]) return strdup(".");
655 const char *slash;
656 #ifdef WIN32
657 slash = NULL;
658 for (const char *ts = s; *ts; ++ts) {
659 if (*ts == '/' || *ts == '\\') slash = ts;
661 if (slash == s && (s[0] == '/' || s[0] == '\\') && (s[1] == '/' || s[1] == '\\')) slash = NULL;
662 #else
663 slash = strrchr(s, '/');
664 #endif
665 if (!slash) return strdup(".");
666 ptrdiff_t len = (ptrdiff_t)(slash-s)+1;
667 char *res = malloc(len+1);
668 memcpy(res, s, len);
669 res[len] = 0;
670 #ifdef WIN32
671 while (len > 0 && (res[len-1] == '\\' || res[len-1] == '/')) --len;
672 #else
673 while (len > 0 && res[len-1] == '/') --len;
674 #endif
675 if (len == 0) { free(res); return strdup("."); }
676 res[len] = 0;
677 return res;
681 static void loadCurSrcLine (void) {
682 if (currSrcLine) {
683 strcpy(currLine, currSrcLine->line);
684 /* macros will not change include dirs */
685 if (!curmacro) {
686 char **incpp = (!currSrcLine->system ? &lastIncludePath : &lastSysIncludePath);
687 if (*incpp) free(*incpp);
688 *incpp = extractFileDir(currSrcLine->fname);
690 normalizeStr(currLine);
691 } else {
692 currLine[0] = 0;
697 UR_FORCE_INLINE SourceLine *setCurSrcLine (SourceLine *l) {
698 currSrcLine = l;
699 loadCurSrcLine();
700 return l;
704 UR_FORCE_INLINE SourceLine *setUFOCurSrcLine (SourceLine *l) {
705 currSrcLine = l;
706 if (currSrcLine) {
707 strcpy(currLine, currSrcLine->line);
708 } else {
709 currLine[0] = 0;
711 return l;
715 UR_FORCE_INLINE SourceLine *nextSrcLine (void) {
716 return (currSrcLine != NULL ? setCurSrcLine(currSrcLine->next) : NULL);
720 UR_FORCE_INLINE SourceLine *nextUFOSrcLine (void) {
721 return (currSrcLine != NULL ? setUFOCurSrcLine(currSrcLine->next) : NULL);
725 UR_FORCE_INLINE int strHasPathDelim (const char *s) {
726 if (!s || !s[0]) return 0;
727 #ifdef WIN32
728 return (strchr(s, '/') || strchr(s, '\\') ? 1 : 0);
729 #else
730 return (strchr(s, '/') ? 1 : 0);
731 #endif
735 /* returns malloced string */
736 static char *createIncludeName (const char *fname, int assystem, const char *defaultmain,
737 const char *lastIncPathUFO) {
738 if (!fname || !fname[0]) return NULL;
739 char *res;
740 if (fname[0] != '/') {
741 const char *incdir;
742 if (!assystem) {
743 incdir = lastIncludePath;
744 } else if (assystem == -669) { // ufo normal
745 incdir = lastIncPathUFO;
746 } else if (assystem == -666) { // ufo system
747 incdir = lastIncPathUFO;
748 if (!incdir || !incdir[0]) incdir = ufoIncludeDir;
749 } else {
750 incdir = lastSysIncludePath;
751 if (!incdir || !incdir[0]) incdir = sysIncludeDir;
753 if (incdir == NULL || incdir[0] == 0) incdir = ".";
754 res = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
755 } else {
756 res = strprintf("%s", fname);
758 struct stat st;
759 if (defaultmain && defaultmain[0]) {
760 if (stat(res, &st) == 0) {
761 if (S_ISDIR(st.st_mode)) {
762 char *rs = strprintf("%s/%s", res, defaultmain);
763 free(res);
764 res = rs;
768 /* check if there is the disk file */
769 if (strHasPathDelim(fname) && stat(res, &st) != 0) {
770 /* no file, try "root include" */
771 const char *incdir = (!assystem || assystem == -669 ? NULL : assystem == -666 ? ufoIncludeDir : sysIncludeDir);
772 char *rs = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
773 free(res);
774 res = rs;
775 /* check for dir again */
776 if (defaultmain && defaultmain[0]) {
777 if (stat(res, &st) == 0) {
778 if (S_ISDIR(st.st_mode)) {
779 char *rs = strprintf("%s/%s", res, defaultmain);
780 free(res);
781 res = rs;
786 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
787 return res;
791 static int includeCount = 0;
793 // process 'INCLUDE'
794 // include file instead of the current line
795 // returns 0 on ok, <0 on error
796 static int asmTextInclude (const char *fname, int system, int softinclude) {
797 char *fn = createIncludeName(fname, system, "zzmain.zas", NULL);
799 FILE *fl = fopen(fn, "r");
800 if (!fl) {
801 if (softinclude) return 0;
802 fprintf(stderr, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", currSrcLine->fname, currSrcLine->lineNo, currLine);
803 free(fn);
804 return -1;
807 if (includeCount > 256) {
808 fclose(fl);
809 free(fn);
810 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", currSrcLine->fname, currSrcLine->lineNo);
811 return -1;
814 ++includeCount;
815 printf("loading: %s\n", fn);
817 int error;
818 SourceLine *first = loadTextFile(fl, system, fn, &error);
819 fclose(fl);
820 free(fn);
822 if (error) return -1;
824 --includeCount;
825 currSrcLine->line[0] = 0; // remove `include` directive
827 SourceLine *last = first;
828 if (last) {
829 while (last->next) last = last->next;
830 last->next = currSrcLine->next;
831 currSrcLine->next = first;
833 return 0;
837 ///////////////////////////////////////////////////////////////////////////////
838 // prototypes
840 static void processCurrentLine (void); // only one, will skip to next one
843 ///////////////////////////////////////////////////////////////////////////////
844 // error raisers, etc
846 static jmp_buf errJP;
849 static void errorWriteFile (FILE *fo) {
850 if (currSrcLine) {
851 size_t slen = strlen(currSrcLine->line);
852 fprintf(fo, "at file %s, line %d\n%.70s%s\n*", currSrcLine->fname,
853 currSrcLine->lineNo, currSrcLine->line, (slen > 70 ? " ..." : ""));
854 } else {
855 fprintf(fo, "somewhere in time: ");
860 static void errorMsgV (const char *fmt, va_list ap) {
861 errorWriteFile(stderr);
862 vfprintf(stderr, fmt, ap);
863 va_end(ap);
864 fputc('\n', stderr);
865 fflush(stderr);
869 __attribute__((format(printf, 1, 2)))
870 static void warningMsg (const char *fmt, ...) {
871 va_list ap;
872 fprintf(stderr, "WARNING ");
873 va_start(ap, fmt);
874 errorMsgV(fmt, ap);
878 __attribute__((format(printf, 1, 2)))
879 static void errorMsg (const char *fmt, ...) {
880 va_list ap;
881 fprintf(stderr, "FATAL ");
882 va_start(ap, fmt);
883 errorMsgV(fmt, ap);
887 __attribute__((noreturn)) __attribute__((format(printf, 1, 2)))
888 static void fatal (const char *fmt, ...) {
889 va_list ap;
890 va_start(ap, fmt);
891 errorMsgV(fmt, ap);
892 longjmp(errJP, 666);
896 __attribute__((noreturn))
897 static void fatalUrLib (int errcode) {
898 errorMsg("%s", urasm_errormsg(errcode));
899 longjmp(errJP, 666);
903 ///////////////////////////////////////////////////////////////////////////////
904 // writer options
905 static int optWriteType = 't';
906 static int optWTChanged = 0;
907 static int optWriteFixups = 0;
908 static int optFixupType = 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
909 static int optRunTape = 1;
910 static int optRunDMB = 1;
911 static int optRunSCL = -1; /* -1: save cargador, but no boot; 1: boot and cargador */
912 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
913 static int optSNA48 = 1;
914 static char *optOutputDir = NULL;
915 static char *optOutputFile = NULL;
916 static int optOutputFileFixed = 0;
919 static char *buildOutputFileName (const char *basename, const char *ext) {
920 ur_assert(ext);
921 ur_assert(basename);
922 if (ext[0] == '.') ++ext;
923 //fprintf(stderr, "********** <%s> <%s> <%s> %d <%s>\n", basename, ext, optOutputFile, optOutputFileFixed, optOutputDir);
924 if (optOutputFile) {
925 if (!optOutputFileFixed) {
926 optOutputFileFixed = 1;
927 char *spp = strrchr(optOutputFile, '/');
928 #ifdef WIN32
929 char *spp1 = strrchr(optOutputFile, '\\');
930 if (!spp || (spp < spp1)) spp = spp1;
931 #endif
932 if (!spp) spp = optOutputFile; else ++spp;
933 char *dotpos = strrchr(spp, '.');
934 if (dotpos) *dotpos = 0;
936 if (optOutputFile && optOutputFile[0]) return strprintf("%s/%s.%s", optOutputDir, optOutputFile, ext);
938 return strprintf("%s/%s.%s", optOutputDir, basename, ext);
942 typedef struct OutDataFile_t {
943 char *name;
944 char type; // 'C', 'B'
945 int hasstart;
946 uint16_t start; // start address, or basic start line
947 uint16_t size; // data size (never 0)
948 uint8_t *data;
949 struct OutDataFile_t *next;
950 } OutDataFile;
952 static OutDataFile *datafiles = NULL;
955 static OutDataFile *appendDataData (const void *bytes, uint16_t start, uint16_t count) {
956 if (!count) return NULL;
958 OutDataFile *off = malloc(sizeof(OutDataFile));
959 off->name = NULL;
960 off->type = 'C';
961 off->hasstart = 0;
962 off->start = start;
963 off->size = count;
964 off->data = malloc(count);
965 if (bytes) memcpy(off->data, bytes, count); else memset(off->data, 0, count);
966 off->next = NULL;
968 OutDataFile *last = datafiles;
969 if (!last) {
970 datafiles = off;
971 } else {
972 while (last->next) last = last->next;
973 last->next = off;
976 return off;
980 static OutDataFile *appendDataFile (const char *fname, long ofs, int len, int allowskip) {
981 if (!fname || !fname[0]) return NULL;
983 FILE *fl = fopen(fname, "rb");
984 if (!fl) {
985 if (allowskip) return NULL;
986 fatal("cannot open data file '%s'", fname);
988 if (fseek(fl, 0, SEEK_END) < 0) fatal("error reading data file '%s'", fname);
989 long fsize = ftell(fl);
990 if (fsize < 0) fatal("error reading data file '%s'", fname);
991 if (ofs < 0) {
992 // from file end
993 ofs = -ofs;
994 if (ofs < 0) fatal("invalid offset");
995 if (ofs >= fsize) ofs = 0; else ofs = fsize-ofs;
997 if (ofs >= fsize) { fclose(fl); return NULL; }
998 if (fseek(fl, ofs, SEEK_SET) < 0) fatal("error reading data file '%s'", fname);
999 fsize -= ofs;
1000 if (len < 0) {
1001 if (fsize > 65535) fatal("data file '%s' too big", fname);
1002 len = (int)fsize;
1004 if (len > 65535) fatal("data file '%s' too big", fname);
1005 if (len == 0) { fclose(fl); return NULL; }
1007 OutDataFile *off = malloc(sizeof(OutDataFile));
1008 off->name = NULL;
1009 off->type = 'C';
1010 off->hasstart = 0;
1011 off->start = 0;
1012 off->size = (uint16_t)len;
1013 off->data = malloc(len);
1014 off->next = NULL;
1015 if (fread(off->data, (unsigned)len, 1, fl) != 1) fatal("error reading data file '%s'", fname);
1016 fclose(fl);
1018 OutDataFile *last = datafiles;
1019 if (!last) {
1020 datafiles = off;
1021 } else {
1022 while (last->next) last = last->next;
1023 last->next = off;
1026 return off;
1030 //////////////////////////////////////////////////////////////////////////////
1031 // operator management
1033 // return !0 to skip current line
1034 typedef int (*UrAsmOpFn) (void);
1036 enum {
1037 PI_CONT_LINE = 0,
1038 PI_SKIP_LINE = 1
1041 typedef struct UrAsmOp_t {
1042 uint32_t hash;
1043 struct UrAsmOp_t *hlink;
1044 // other
1045 char *name;
1046 UrAsmOpFn fn;
1047 void *udata; // user data (struct info for structs, for example)
1048 struct UrAsmOp_t *next;
1049 } UrAsmOp;
1051 #define UR_OPER_BUCKETS (256)
1052 static UrAsmOp *oplist = NULL;
1053 static UrAsmOp *urCurrentOp = NULL;
1054 static UrAsmOp *operBuckets[UR_OPER_BUCKETS];
1057 //==========================================================================
1059 // urAddOpEx
1061 //==========================================================================
1062 static UrAsmOp *urAddOpEx (const char *name, UrAsmOpFn fn, char optpfx) {
1063 ur_assert(name != NULL && name[0] != 0);
1064 if (oplist == NULL) {
1065 //fprintf(stderr, "CLEAR OPBUCKETS!\n");
1066 for (int f = 0; f < UR_OPER_BUCKETS; f += 1) operBuckets[f] = NULL;
1068 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
1069 ur_assert(res);
1070 if (optpfx != 0) {
1071 res->name = calloc(1, strlen(name) + 2);
1072 ur_assert(res->name);
1073 res->name[0] = optpfx;
1074 strcpy(res->name + 1, name);
1075 } else {
1076 res->name = strdup(name);
1077 ur_assert(res->name);
1079 res->fn = fn;
1080 res->udata = NULL;
1081 res->next = oplist;
1082 oplist = res;
1083 // insert into hash table
1084 res->hash = joaatHashStrCI(res->name);
1085 const uint32_t bkt = res->hash%UR_OPER_BUCKETS;
1086 //fprintf(stderr, "NEWOP: 0x%08x (%u) : <%s>\n", res->hash, bkt, res->name);
1087 res->hlink = operBuckets[bkt];
1088 operBuckets[bkt] = res;
1089 return res;
1093 //==========================================================================
1095 // urAddOp
1097 //==========================================================================
1098 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
1099 return urAddOpEx(name, fn, 0);
1103 //==========================================================================
1105 // urAddOpAndDollar
1107 //==========================================================================
1108 static void urAddOpAndDollar (const char *name, UrAsmOpFn fn) {
1109 (void)urAddOpEx(name, fn, 0);
1110 (void)urAddOpEx(name, fn, '$');
1114 //==========================================================================
1116 // urFindOp
1118 //==========================================================================
1119 static UrAsmOp *urFindOp (const char *name) {
1120 if (name == NULL || name[0] == 0) return NULL;
1121 #if 0
1122 UrAsmOp *res;
1123 if (name[0] == '$' && name[1]) {
1124 for (res = oplist; res; res = res->next) {
1125 if (res->name[0] == '$') {
1126 if (strEquCI(name, res->name)) break;
1127 } else {
1128 if (strEquCI(name+1, res->name)) break;
1131 } else {
1132 for (res = oplist; res; res = res->next) if (strEquCI(name, res->name)) break;
1134 return res;
1135 #else
1136 const uint32_t hash = joaatHashStrCI(name);
1137 //fprintf(stderr, "LOOK: 0x%08x (%u) : <%s>\n", hash, hash%UR_OPER_BUCKETS, name);
1138 UrAsmOp *res = operBuckets[hash%UR_OPER_BUCKETS];
1139 while (res != NULL && (res->hash != hash || !strEquCI(name, res->name))) {
1140 //fprintf(stderr, " REJECT: 0x%08x: <%s>\n", res->hash, res->name);
1141 res = res->hlink;
1143 return res;
1144 #endif
1148 //==========================================================================
1150 // urClearOps
1152 //==========================================================================
1153 static void urClearOps (void) {
1154 while (oplist != NULL) {
1155 UrAsmOp *c = oplist;
1156 oplist = oplist->next;
1157 free(c->name);
1158 free(c);
1160 for (int f = 0; f < UR_OPER_BUCKETS; f += 1) operBuckets[f] = NULL;
1164 ///////////////////////////////////////////////////////////////////////////////
1165 // label management
1167 enum {
1168 LBL_TYPE_VERY_SPECIAL = -42,
1169 LBL_TYPE_UNKNOWN = -1,
1170 LBL_TYPE_ASS = 0, // `=`
1171 LBL_TYPE_EQU = 1, // equ
1172 LBL_TYPE_CODE = 2, // ':', or "lbl opcode"
1173 LBL_TYPE_STOFS = 3, // struct offset
1174 LBL_TYPE_DATA = 4,
1177 typedef struct UrLabelInfo_t {
1178 uint32_t hash;
1179 struct UrLabelInfo_t *hlink;
1180 // other
1181 char *name;
1182 int32_t value;
1183 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
1184 int known; /* !0: label value already known */
1185 int refLine; /* first referenced line */
1186 int fixuptype; /* UR_FIXUP_XXX */
1187 int modHidden; /* hidden module label? hack for PLUS3BOOT */
1188 char savedFirstChar; /* for hidden labels */
1189 char *refFile;
1190 struct UrLabelInfo_t *next;
1191 // previous temp label
1192 struct UrLabelInfo_t *prevTemp;
1193 int tempIdx;
1194 int tempFwd;
1195 } UrLabelInfo;
1198 #define UR_LABEL_BUCKETS (256)
1199 static UrLabelInfo *labels = NULL;
1200 static UrLabelInfo *labelsTail = NULL;
1201 static UrLabelInfo *labelBuckets[UR_LABEL_BUCKETS];
1202 // list of all @@, from the last defined
1203 static UrLabelInfo *tempLabels = NULL;
1204 static int tempLabelIdx = 0;
1207 //==========================================================================
1209 // initLabelBuckets
1211 //==========================================================================
1212 static void initLabelBuckets (void) {
1213 for (int f = 0; f < UR_LABEL_BUCKETS; f += 1) labelBuckets[f] = NULL;
1217 //==========================================================================
1219 // urClearLabels
1221 //==========================================================================
1222 static void urClearLabels (void) {
1223 UrLabelInfo *c;
1224 while ((c = labels) != NULL) {
1225 labels = c->next;
1226 if (c->name) free(c->name);
1227 if (c->refFile) free(c->refFile);
1228 free(c);
1230 labelsTail = NULL;
1231 initLabelBuckets();
1235 //==========================================================================
1237 // urFindLabel
1239 //==========================================================================
1240 static UrLabelInfo *urFindLabel (const char *name) {
1241 if (name != NULL && name[0] != 0) {
1242 #if 0
1243 for (UrLabelInfo *c = labels; c; c = c->next) {
1244 if (strcmp(name, c->name) == 0) return c;
1246 #else
1247 const uint32_t hash = joaatHashStr(name);
1248 UrLabelInfo *c = labelBuckets[hash%UR_LABEL_BUCKETS];
1249 while (c != NULL) {
1250 if (c->hash == hash && strcmp(c->name, name) == 0) return c;
1251 c = c->hlink;
1253 #endif
1255 return NULL;
1259 //==========================================================================
1261 // urAddLabel
1263 //==========================================================================
1264 static UrLabelInfo *urAddLabel (const char *name) {
1265 ur_assert(name != NULL && name[0] != 0);
1266 UrLabelInfo *c = urFindLabel(name);
1267 if (c == NULL) {
1268 c = calloc(1, sizeof(UrLabelInfo));
1269 ur_assert(c);
1270 c->name = strdup(name);
1271 ur_assert(c->name);
1272 c->type = LBL_TYPE_UNKNOWN;
1273 c->fixuptype = UR_FIXUP_NONE;
1274 c->modHidden = 0;
1275 c->savedFirstChar = 0;
1276 c->next = NULL;
1277 if (labelsTail) labelsTail->next = c; else labels = c;
1278 labelsTail = c;
1279 c->hash = joaatHashStr(c->name);
1280 const uint32_t bkt = c->hash%UR_LABEL_BUCKETS;
1281 c->hlink = labelBuckets[bkt];
1282 labelBuckets[bkt] = c;
1284 return c;
1288 //==========================================================================
1290 // urAddTempLabel
1292 //==========================================================================
1293 static UrLabelInfo *urAddTempLabel (void) {
1294 char name[32];
1295 tempLabelIdx += 1;
1296 snprintf(name, sizeof(name), "@@_%05d", tempLabelIdx);
1297 UrLabelInfo *lbl = urAddLabel(name);
1298 lbl->tempIdx = tempLabelIdx;
1299 lbl->prevTemp = tempLabels;
1300 tempLabels = lbl;
1301 return lbl;
1305 //==========================================================================
1307 // urNextTempLabel
1309 //==========================================================================
1310 static UrLabelInfo *urNextTempLabel (void) {
1311 char name[32];
1312 tempLabelIdx += 1;
1313 snprintf(name, sizeof(name), "@@_%05d", tempLabelIdx);
1314 UrLabelInfo *lbl = urFindLabel(name);
1315 ur_assert(lbl->tempIdx == tempLabelIdx);
1316 ur_assert(lbl->prevTemp == NULL);
1317 lbl->prevTemp = tempLabels;
1318 tempLabels = lbl;
1319 return lbl;
1323 //==========================================================================
1325 // urResetTempLabels
1327 //==========================================================================
1328 static void urResetTempLabels (void) {
1329 while (tempLabels != NULL) {
1330 UrLabelInfo *lbl = tempLabels;
1331 tempLabels = lbl->prevTemp;
1332 lbl->prevTemp = NULL;
1334 tempLabels = NULL;
1335 tempLabelIdx = 0;
1339 //==========================================================================
1341 // urAddTempLocal
1343 // add local label; it may shadow others
1345 //==========================================================================
1346 static UrLabelInfo *urAddTempLocal (const char *name) {
1347 UrLabelInfo *c = calloc(1, sizeof(UrLabelInfo));
1348 ur_assert(c);
1349 c->name = strdup(name);
1350 ur_assert(c->name);
1351 c->type = LBL_TYPE_EQU;
1352 c->known = 1;
1353 c->fixuptype = UR_FIXUP_NONE;
1354 c->modHidden = 0;
1355 c->savedFirstChar = 0;
1356 c->next = labels;
1357 labels = c;
1358 if (!labelsTail) labelsTail = c;
1359 c->hash = joaatHashStr(c->name);
1360 const uint32_t bkt = c->hash%UR_LABEL_BUCKETS;
1361 c->hlink = labelBuckets[bkt];
1362 labelBuckets[bkt] = c;
1363 return c;
1367 static void UFOZXLabelRemoved (UrLabelInfo *lbl);
1370 //==========================================================================
1372 // urRemoveTempLocal
1374 //==========================================================================
1375 static void urRemoveTempLocal (UrLabelInfo *tmplbl) {
1376 if (!tmplbl) return;
1377 ur_assert(tmplbl == labels);
1378 UFOZXLabelRemoved(tmplbl);
1379 labels = tmplbl->next;
1380 if (!labels) labelsTail = NULL;
1381 const uint32_t bkt = tmplbl->hash%UR_LABEL_BUCKETS;
1382 ur_assert(labelBuckets[bkt] == tmplbl);
1383 labelBuckets[bkt] = tmplbl->hlink;
1384 free(tmplbl->name);
1385 free(tmplbl);
1389 ///////////////////////////////////////////////////////////////////////////////
1390 // structures
1391 typedef struct StructField_s {
1392 struct StructField_s *next; // next field
1393 char *name; // field name
1394 uint16_t ofs; // field offset in struct
1395 uint16_t size; // field size in bytes (can be 0 for aliases)
1396 int32_t initValue;
1397 int hasInit;
1398 } StructField;
1401 typedef struct StructInfo_s {
1402 struct StructInfo_s *next; // next structure
1403 char *name; // structure name
1404 StructField *fields;
1405 uint16_t size; // total structure size in bytes
1406 int zerofill;
1407 } StructInfo;
1410 // list of all structures
1411 static StructInfo *structList = NULL;
1414 static void freeStructs (void) {
1415 while (structList != NULL) {
1416 StructInfo *sth = structList;
1417 structList = sth->next;
1418 StructField *fld = sth->fields;
1419 while (fld) {
1420 StructField *fc = fld;
1421 fld = fld->next;
1422 if (fc->name) free(fc->name);
1423 free(fc);
1425 if (sth->name) free(sth->name);
1426 free(sth);
1431 ///////////////////////////////////////////////////////////////////////////////
1432 // fixup management
1433 typedef struct FixupItem_s {
1434 struct FixupItem_s *next;
1435 uint16_t opdestaddr;
1436 uint16_t opaddr;
1437 int fixuptype;
1438 int size;
1439 } FixupItem;
1440 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
1443 static void clearFixups (void) {
1444 FixupItem *c;
1445 while ((c = fixlisthead) != NULL) {
1446 fixlisthead = c->next;
1447 free(c);
1449 fixlisthead = fixlisttail = NULL;
1453 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
1454 FixupItem *fx = calloc(1, sizeof(FixupItem));
1456 fx->opdestaddr = opdestaddr;
1457 fx->opaddr = opaddr;
1458 fx->fixuptype = fixuptype;
1459 fx->size = size;
1461 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
1462 fx->next = NULL;
1463 fixlisttail = fx;
1467 ///////////////////////////////////////////////////////////////////////////////
1468 // destination memory management
1470 static uint8_t memory[65536];
1471 static uint8_t memused[65536];
1472 static uint8_t memresv[65536];
1473 static uint16_t start_pc = 0x100; // viva CP/M!
1474 static uint16_t start_disp = 0x100; // viva CP/M!
1475 static uint16_t start_ent = 0x100; // viva CP/M!
1476 static uint16_t pc = 0; /* current position to write */
1477 static uint16_t disp = 0; /* current 'virtual PC' */
1478 static uint16_t ent = 0; /* starting address */
1479 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
1480 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
1481 static int inTapeBlock = 0;
1482 static uint8_t tapeXorB = 0;
1484 static int findChunkFrom (int addr, int *start, int *len);
1487 // find next free area starting from `addr`
1488 // returns address or -1
1489 static int reserveFindFreeFrom (uint16_t addr) {
1490 for (uint32_t freeaddr = addr; freeaddr <= 0xffffU; ++freeaddr) {
1491 if (!memresv[freeaddr]) return freeaddr;
1493 return -1;
1497 // find next reserved area starting from `addr`
1498 // returns address or -1
1499 static int reserveFindReservedFrom (uint16_t addr) {
1500 for (uint32_t freeaddr = addr; freeaddr <= 0xffffU; ++freeaddr) {
1501 if (memresv[freeaddr]) return freeaddr;
1503 return -1;
1507 static void reserveReleaseUsed (uint16_t addr, int len) {
1508 while (len--) memused[addr++] = 0;
1512 //TODO: wrapping
1513 static int reserveHit (uint16_t addr, int len) {
1514 if (len <= 0) return 0;
1515 while (len--) {
1516 if (memresv[addr++]) return 1;
1518 return 0;
1522 // start from `addr`, look for free areas, and check
1523 // if we can insert jump from a pcaddr there
1524 // returns next free area or -1
1525 static int reservedFindNextJumpableFreeFrom (uint16_t pcaddr, uint16_t addr) {
1526 // check if there is enough room at least for a jr
1527 if (memresv[pcaddr] || memresv[pcaddr+1]) return -1;
1528 uint16_t caddr = addr;
1529 // skip reserved
1530 while (caddr <= 0xffffU && memresv[caddr]) ++caddr;
1531 if (caddr > 0xffffU) return -1; // alas
1532 // check distance
1533 uint32_t jrdist = caddr-(pcaddr+2U);
1534 if (jrdist < 128u) return (int)caddr; // yay, we can JR there, this free area is good
1535 // cannot JR, check if we have room for JP
1536 if (memresv[pcaddr+2]) return -1; // alas
1537 // ok, can JP
1538 return (int)caddr;
1542 // start from `addr`, look for free areas, and check
1543 // if we can insert jump from a pcaddr there, and if
1544 // that area is big enough to hold a next jump and at least one more byte
1545 // returns next free area or -1
1546 static int reservedFindNextSuitableFreeFrom (uint16_t pcaddr, uint16_t addr) {
1547 int caddr = addr;
1548 for (;;) {
1549 caddr = reservedFindNextJumpableFreeFrom(pcaddr, (uint16_t)caddr);
1550 if (caddr < 0) return -1; // alas
1551 // we can jump to naddr, now check if it is big enough
1552 // if it has less than 3 bytes, it is too small
1553 if (memresv[(uint16_t)(caddr+1)] || memresv[(uint16_t)(caddr+2)]) {
1554 // too small
1555 caddr += 3;
1556 continue;
1558 // if we have more than 3 bytes, it is definitely ok
1559 if (!memresv[(uint16_t)(caddr+3)]) return caddr; // ok
1560 // it has exactly 3 bytes, check if it is enough
1561 int xaddr = reservedFindNextJumpableFreeFrom(caddr+1, (uint16_t)caddr+3);
1562 if (xaddr >= 0) return caddr; // ok
1563 // alas
1564 caddr = reserveFindReservedFrom(caddr);
1565 if (caddr < 0) return -1;
1566 caddr = reserveFindFreeFrom(caddr);
1567 if (caddr < 0) return -1;
1571 UR_FORCE_INLINE uint8_t getByte (uint16_t addr) {
1572 return memory[addr];
1576 UR_FORCE_INLINE uint16_t getWord (uint16_t addr) {
1577 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
1581 UR_FORCE_INLINE void putByte (uint16_t addr, uint8_t b) {
1582 if (inTapeBlock) tapeXorB ^= b;
1583 memory[addr] = b;
1584 memused[addr] = 1;
1588 UR_FORCE_INLINE void putWord (uint16_t addr, uint16_t w) {
1589 putByte(addr, w&0xFFU);
1590 putByte(addr+1, (w>>8)&0xFFU);
1594 UR_FORCE_INLINE void emitByte (uint8_t b) {
1595 putByte(pc, b);
1596 ++pc;
1597 ++disp;
1601 UR_FORCE_INLINE void emitWord (uint16_t w) {
1602 emitByte(w&0xFFU);
1603 emitByte((w>>8)&0xFFU);
1607 UR_FORCE_INLINE void emitRWord (uint16_t w) {
1608 emitByte((w>>8)&0xFFU);
1609 emitByte(w&0xFFU);
1613 static void prepareMemory (void) {
1614 memset(memory, 0, sizeof(memory));
1615 memset(memused, 0, sizeof(memused));
1616 memset(memresv, 0, sizeof(memresv));
1620 ///////////////////////////////////////////////////////////////////////////////
1621 // module list management
1623 typedef struct ModuleInfo_t {
1624 char *name;
1625 char *fname; // opened in this file
1626 int seen; // !0: module already seen, skip other definitions from the same file
1627 struct ModuleInfo_t *next;
1628 uint32_t hash;
1629 struct ModuleInfo_t *hlink;
1630 } ModuleInfo;
1632 #define UR_MODULE_BUCKETS (1024)
1633 static ModuleInfo *modules = NULL;
1634 static ModuleInfo *currModule = NULL;
1635 static ModuleInfo *moduleBuckets[UR_MODULE_BUCKETS];
1637 static uint8_t mod_memory_saved[65536];
1638 static uint8_t mod_memused_saved[65536];
1639 static uint8_t mod_memresv_saved[65536];
1642 static void modulesClear (void) {
1643 currModule = NULL;
1644 while (modules) {
1645 ModuleInfo *c = modules;
1646 modules = modules->next;
1647 free(c->name);
1648 free(c->fname);
1649 free(c);
1651 for (int f = 0; f < UR_MODULE_BUCKETS; f +=1) moduleBuckets[f] = NULL;
1655 //==========================================================================
1657 // modulesResetSeen
1659 //==========================================================================
1660 static void modulesResetSeen (void) {
1661 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
1665 //==========================================================================
1667 // moduleFind
1669 //==========================================================================
1670 static ModuleInfo *moduleFind (const char *name) {
1671 if (!name || !name[0]) return NULL;
1672 #if 0
1673 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
1674 #else
1675 const uint32_t hash = joaatHashStr(name);
1676 ModuleInfo *c = moduleBuckets[hash%UR_MODULE_BUCKETS];
1677 while (c != NULL) {
1678 if (c->hash == hash && strcmp(c->name, name) == 0) return c;
1679 c = c->hlink;
1681 #endif
1682 return NULL;
1686 //==========================================================================
1688 // moduleAdd
1690 // special module name: PLUS3BOOT
1692 //==========================================================================
1693 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
1694 ModuleInfo *c;
1695 ur_assert(name && fname && name[0] && fname[0]);
1696 c = calloc(1, sizeof(ModuleInfo));
1697 ur_assert(c);
1698 c->name = strdup(name);
1699 ur_assert(c->name);
1700 c->fname = strdup(fname);
1701 ur_assert(c->fname);
1702 c->next = modules;
1703 modules = c;
1704 c->hash = joaatHashStr(name);
1705 const uint32_t bkt = c->hash%UR_MODULE_BUCKETS;
1706 c->hlink = moduleBuckets[bkt];
1707 moduleBuckets[bkt] = c;
1708 // +3DOS bootsector?
1709 if (strEquCI(c->name, "PLUS3BOOT")) {
1710 // save current memory state, because bootsector will be written as a separate independent file
1711 memcpy(mod_memory_saved, memory, sizeof(mod_memory_saved));
1712 memcpy(mod_memused_saved, memused, sizeof(mod_memused_saved));
1713 memcpy(mod_memresv_saved, memresv, sizeof(mod_memresv_saved));
1715 return c;
1719 //==========================================================================
1721 // moduleOpen
1723 //==========================================================================
1724 static void moduleOpen (void) {
1725 //fprintf(stderr, "::: OPENING MODULE <%s> :::\n", currModule->name);
1726 // unhide +3DOS boot labels
1727 if (strEquCI(currModule->name, "PLUS3BOOT")) {
1728 for (UrLabelInfo *lbl = labels; lbl; lbl = lbl->next) {
1729 if (lbl->modHidden == 1) {
1730 if (!lbl->savedFirstChar) fatal("internal module manager error");
1731 lbl->modHidden = 0;
1732 lbl->name[0] = lbl->savedFirstChar; // unhide it
1733 //fprintf(stderr, "***UN-HIDDEN: <%s>\n", lbl->name);
1740 //==========================================================================
1742 // isModuleLabel
1744 //==========================================================================
1745 static int isModuleLabel (const ModuleInfo *mi, const char *name) {
1746 if (!mi || !name || !name[0]) return 0;
1747 if (name[0] == '.') ++name;
1748 const size_t mnlen = strlen(mi->name);
1749 if (strlen(name) <= mnlen || name[mnlen] != '.') return 0;
1750 return (memcmp(name, mi->name, mnlen) == 0);
1754 //==========================================================================
1756 // moduleClose
1758 // close current module
1760 //==========================================================================
1761 static void moduleClose (const char *mname) {
1762 if (mname && !mname[0]) mname = NULL;
1763 if (!currModule) {
1764 if (!mname) fatal("trying to close unopened module");
1765 fatal("trying to close unopened module '%s'", mname);
1767 if (mname && strcmp(mname, currModule->name)) fatal("invalid module name in ENDMODULE; got '%s', expected '%s'", mname, currModule->name);
1769 // +3DOS bootsector?
1770 if (strEquCI(currModule->name, "PLUS3BOOT")) {
1771 // append bootsector chunk
1772 int start = 0, len;
1774 if (!findChunkFrom(start, &start, &len)) fatal("no +3DOS bootsector chunk");
1775 if (start != 0xFE10) fatal("+3DOS bootsector chunk must be 'org'ed and #FE10");
1776 if (len > 512-16) fatal("+3DOS bootsector chunk is too big");
1778 if (pass == 1) {
1779 OutDataFile *off = appendDataData(memory+(unsigned)start, (unsigned)start, (unsigned)len);
1780 if (!off) fatal("cannot append +3DOS bootsector");
1781 off->name = strdup("");
1782 off->type = 1; // special type
1784 start += len;
1785 if (findChunkFrom(start, &start, &len)) fatal("only +3DOS bootsector chunk is allowed (found #%04X, %d)", (unsigned)start, len);
1788 // restore memory
1789 memcpy(memory, mod_memory_saved, sizeof(memory));
1790 memcpy(memused, mod_memused_saved, sizeof(memused));
1791 memcpy(memresv, mod_memresv_saved, sizeof(memresv));
1793 // remove module labels
1794 for (UrLabelInfo *lbl = labels; lbl; lbl = lbl->next) {
1795 if (!isModuleLabel(currModule, lbl->name)) continue;
1796 //fprintf(stderr, "***HIDDEN: <%s>\n", lbl->name);
1797 lbl->modHidden = 1;
1798 lbl->savedFirstChar = lbl->name[0];
1799 lbl->name[0] = '{'; // hide it
1803 currModule = NULL;
1807 ///////////////////////////////////////////////////////////////////////////////
1808 // label getter and utilities
1810 static char *lastSeenGlobalLabel = NULL; /* global; malloced */
1813 static char *mangleLabelName (const char *name) {
1814 static char newname[MAX_LINE_SIZE*2+1024];
1815 if (!name || !name[0]) {
1816 newname[0] = '\0';
1817 return newname;
1819 // local label or macro label?
1820 if (name[0] == '.') {
1821 // two dots is a macro label
1822 if (name[1] == '.') {
1823 if (!name[2]) fatal("invalid label '%s'", name);
1824 // this is macro label
1825 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
1826 snprintf(newname, sizeof(newname), ".%s.MACLBL%d%s", curmacro->mac->name, curmacronum, name+1);
1827 return newname;
1829 if (!name[1]) fatal("invalid label '%s'", name);
1830 // simple local
1831 if (!lastSeenGlobalLabel) fatal("local label '%s' without a global", name);
1832 snprintf(newname, sizeof(newname), ".%s%s", lastSeenGlobalLabel, name);
1833 return newname;
1835 // global label
1836 // if global is prefixed with '@', it should not be mangled
1837 // this is to allow access to other labels from modules
1838 if (name[0] == '@') {
1839 if (name[1] == '@') fatal("double-prefixed label '%s'", name);
1840 if (!name[1]) fatal("invalid label '%s'", name);
1841 snprintf(newname, sizeof(newname), "%s", name+1);
1842 return newname;
1844 // if no module, nothing to do
1845 // also, do not mangle labels with dots inside:
1846 // those are either already mangled, or call to other modules
1847 if (!currModule || strchr(name, '.')) {
1848 snprintf(newname, sizeof(newname), "%s", name);
1849 return newname;
1851 // this is global unqualified label and we have a module; let's rename it
1852 snprintf(newname, sizeof(newname), "%s.%s", currModule->name, name);
1853 return newname;
1857 static UrLabelInfo *addTempLabel (void) {
1858 UrLabelInfo *lbl = tempLabels;
1859 if (lbl == NULL || !lbl->tempFwd) {
1860 if (pass == 0) {
1861 lbl = urAddTempLabel();
1862 } else {
1863 lbl = urNextTempLabel();
1865 lbl->tempFwd = 1;
1866 if (!lbl->refFile) {
1867 lbl->refLine = currSrcLine->lineNo;
1868 lbl->refFile = strdup(currSrcLine->fname);
1871 return lbl;
1875 static UrLabelInfo *addFwdTempLabel (void) {
1876 return addTempLabel();
1880 // WARNING! this will be invalidated
1881 static char lastFindLabelName[MAX_LINE_SIZE*2+1024];
1884 static UrLabelInfo *findLabel (const char *name) {
1885 UrLabelInfo *lbl;
1886 if (!name || !name[0]) fatal("UrAsm internal error: empty name in `findLabel()`");
1887 // temp label?
1888 if (name[0] == '@') {
1889 if ((name[1] == '@' && name[2] == 'b' && name[3] == 0) ||
1890 (name[1] == 'b' && name[2] == 0))
1892 // last defined temp
1893 lbl = tempLabels;
1894 while (lbl != NULL && lbl->tempFwd) lbl = lbl->prevTemp;
1895 if (lbl == NULL) fatal("no temp labels defined yet");
1896 return lbl;
1898 if ((name[1] == '@' && name[2] == 'f' && name[3] == 0) ||
1899 (name[1] == 'f' && name[2] == 0))
1901 // next defined temp
1902 return addFwdTempLabel();
1905 // try temp label
1906 if (name[0] == '@' && name[1] == '@' && name[2] == '_') {
1907 //fprintf(stderr, "TEMP: <%s>\n", name);
1908 lbl = urFindLabel(name);
1909 if (lbl) return lbl;
1911 char *nn = mangleLabelName(name);
1912 strcpy(lastFindLabelName, nn);
1913 lbl = urFindLabel(nn);
1915 if (!lbl) {
1916 // try non-module label
1917 lbl = urFindLabel(ln);
1920 return lbl;
1924 static UrLabelInfo *findAddLabel (const char *name) {
1925 if (!name || !name[0]) fatal("UrAsm internal error: empty name in `findAddLabel()`");
1926 char *nn = mangleLabelName(name);
1927 strcpy(lastFindLabelName, nn);
1928 UrLabelInfo *lbl = urAddLabel(nn);
1929 if (!lbl->refFile) {
1930 lbl->refLine = currSrcLine->lineNo;
1931 lbl->refFile = strdup(currSrcLine->fname);
1933 return lbl;
1937 static int lblOptMakeU2 = 0;
1939 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
1940 // predefined
1941 if (strEquCI(name, "__URASM_DEFFMT_ANY_DISK")) {
1942 *found = 1;
1943 *defined = 1;
1944 *fixuptype = UR_FIXUP_NONE;
1945 return (optWriteType == 'S' || optWriteType == '3'); // SCL or +3DOS
1947 if (strEquCI(name, "__URASM_DEFFMT_SCL")) {
1948 *found = 1;
1949 *defined = 1;
1950 *fixuptype = UR_FIXUP_NONE;
1951 return (optWriteType == 'S');
1953 if (strEquCI(name, "__URASM_DEFFMT_SCL_MONO")) {
1954 *found = 1;
1955 *defined = 1;
1956 *fixuptype = UR_FIXUP_NONE;
1957 return (optWriteType == 'M');
1959 if (strEquCI(name, "__URASM_DEFFMT_P3DOS")) {
1960 *found = 1;
1961 *defined = 1;
1962 *fixuptype = UR_FIXUP_NONE;
1963 return (optWriteType == '3');
1965 if (strEquCI(name, "__URASM_DEFFMT_HOBETA")) {
1966 *found = 1;
1967 *defined = 1;
1968 *fixuptype = UR_FIXUP_NONE;
1969 return (optWriteType == 'H');
1971 if (strEquCI(name, "__URASM_DEFFMT_TAPE")) {
1972 *found = 1;
1973 *defined = 1;
1974 *fixuptype = UR_FIXUP_NONE;
1975 return (optWriteType == 't');
1977 if (strEquCI(name, "__URASM_DEFFMT_ANY_SNA")) {
1978 *found = 1;
1979 *defined = 1;
1980 *fixuptype = UR_FIXUP_NONE;
1981 return (optWriteType == 's');
1983 if (strEquCI(name, "__URASM_DEFFMT_SNA48")) {
1984 *found = 1;
1985 *defined = 1;
1986 *fixuptype = UR_FIXUP_NONE;
1987 return (optWriteType == 's' && optSNA48);
1989 if (strEquCI(name, "__URASM_DEFFMT_SNA128")) {
1990 *found = 1;
1991 *defined = 1;
1992 *fixuptype = UR_FIXUP_NONE;
1993 return (optWriteType == 's' && !optSNA48);
1995 if (strEquCI(name, "__URASM_DEFFMT_RAW")) {
1996 *found = 1;
1997 *defined = 1;
1998 *fixuptype = UR_FIXUP_NONE;
1999 return (optWriteType == 'r');
2001 if (strEquCI(name, "__URASM_DEFFMT_DMB")) {
2002 *found = 1;
2003 *defined = 1;
2004 *fixuptype = UR_FIXUP_NONE;
2005 return (optWriteType == 'd');
2008 UrLabelInfo *lbl = findLabel(name);
2009 if (!lbl) {
2010 if (pass != 0) {
2011 //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);
2012 errorMsg("using undefined label '%s'", name);
2013 *found = 0;
2014 *defined = 0;
2015 return 0;
2017 lbl = urAddLabel(lastFindLabelName);
2018 lbl->type = (lblOptMakeU2 ? LBL_TYPE_VERY_SPECIAL : LBL_TYPE_UNKNOWN);
2019 lbl->known = 0;
2020 lbl->refLine = currSrcLine->lineNo;
2021 lbl->refFile = strdup(currSrcLine->fname);
2022 //fprintf(stderr, "new label: [%s]\n", lbl->name);
2023 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2024 } else {
2025 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2027 if (lbl) {
2028 *found = 1;
2029 *defined = (lbl->known != 0);
2030 *fixuptype = lbl->fixuptype;
2031 return lbl->value;
2033 *found = 0;
2034 *defined = 0;
2035 return 0;
2039 // qtypes
2040 enum {
2041 UR_QTYPE_DEFINED,
2042 UR_QTYPE_KNOWN
2045 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
2046 UrLabelInfo *lbl = findLabel(name);
2047 switch (qtype) {
2048 case UR_QTYPE_DEFINED: return (lbl ? lbl->known != 0 : 0);
2049 case UR_QTYPE_KNOWN: return (lbl != NULL);
2050 default: ;
2052 return 0;
2056 static void fixupOperandCB (const urasm_operand_t *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
2057 if (pass == 1) {
2058 //static const char *n[4] = {"none", "word", "low", "high"};
2059 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
2060 addFixup(opdestaddr, opaddr, fixuptype, size);
2065 static int checkLabels (void) {
2066 int wasError = 0;
2067 for (UrLabelInfo *c = labels; c; c = c->next) {
2068 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
2069 if (c->type == LBL_TYPE_UNKNOWN) {
2070 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
2071 wasError = 1;
2073 if (c->type == LBL_TYPE_ASS) c->known = -1;
2075 //if (wasError) longjmp(errJP, 667);
2076 return wasError;
2080 ///////////////////////////////////////////////////////////////////////////////
2081 // expression utils (aka string parsing)
2084 /* skip leading spaces */
2085 /* returns string with spaces skipped */
2086 UR_FORCE_INLINE char *strSkipSpaces (const char *s) {
2087 while (*s && isSpace(*s)) ++s;
2088 return (char *)s;
2092 /* skip leading spaces */
2093 /* returns string with spaces skipped */
2094 UR_FORCE_INLINE const char *strSkipSpacesConst (const char *s) {
2095 while (*s && isSpace(*s)) ++s;
2096 return s;
2100 /* remove trailing spaces from string */
2101 static void strTrimRight (char *s) {
2102 if (!s || !s[0]) return;
2103 size_t len = strlen(s);
2104 while (len > 0 && isSpace(s[len-1])) --len;
2105 s[len] = 0;
2109 /* skip leading spaces and colons */
2110 /* returns string with spaces skipped */
2111 UR_FORCE_INLINE char *strSkipSpacesColons (char *s) {
2112 while (*s && (isSpace(*s) || *s == ':')) ++s;
2113 return s;
2117 /* remove leading spaces from the current line */
2118 UR_FORCE_INLINE void removeSpaces (void) {
2119 char *e = strSkipSpaces(currLine);
2120 if (e != currLine) memmove(currLine, e, strlen(e)+1);
2124 /* skip leading spaces, and argument (up to, but not including comma or colon) */
2125 /* correctly skip strings */
2126 /* returns string after skipped argument (with trailing spaces skipped) */
2127 static char *skipMacroArg (char *str) {
2128 int parens = 0;
2129 char *strstart = str;
2130 for (;;) {
2131 str = strSkipSpaces(str);
2132 if (!str[0]) return str;
2133 if (parens == 0 && (str[0] == ',' || str[0] == ':')) return str;
2134 /* check for "af'" */
2135 if (str[0] == '\'' && str-strstart >= 2 && toLower(str[-2] == 'a') && toLower(str[-1] == 'f')) {
2136 ++str;
2137 continue;
2139 if (str[0] == '(') { ++parens; str += 1; continue; }
2140 if (str[0] == ')') { --parens; if (parens < 0) parens = 0; str += 1; continue; }
2141 /* check for string */
2142 if (str[0] == '"' || str[0] == '\'') {
2143 const char qch = *str++;
2144 while (*str) {
2145 const char ch = *str++;
2146 if (ch == qch) break;
2147 if (ch == '\\' && *str) ++str;
2149 continue;
2151 ++str;
2156 /* evaluate next numeric expression in input string */
2157 /* returns expression value */
2158 static int32_t getExprArg (int *defined, int *addr) {
2159 int error = 0;
2160 char *a = strSkipSpaces(currLine);
2161 int32_t res;
2162 if (!a[0]) fatal("expression expected");
2163 const char *ee = urasm_expr(&res, a, disp, defined, addr, &error);
2164 if (error) fatalUrLib(error);
2165 if (*ee) {
2166 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
2167 memmove(currLine, ee, strlen(ee)+1);
2168 } else {
2169 currLine[0] = '\0';
2171 return res;
2175 /* evaluate next string expression in input string */
2176 /* returns expression value */
2177 static char *getStrExprArgFmt (void) {
2178 int error = 0;
2179 int donteval = 0, defined = 0;
2180 static char resbuf[256];
2181 char *a = strSkipSpaces(currLine);
2182 if (!a[0]) fatal("expression expected");
2183 urasm_exprval_t res;
2184 urasm_exprval_init(&res);
2185 const char *ee = urasm_expr_ex(&res, a, disp, &donteval, &defined, &error);
2186 if (error) fatalUrLib(error);
2187 if (*ee) {
2188 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
2189 memmove(currLine, ee, strlen(ee)+1);
2190 } else {
2191 currLine[0] = '\0';
2193 if (res.str) {
2194 snprintf(resbuf, sizeof(resbuf), "%s", res.str);
2195 } else {
2196 snprintf(resbuf, sizeof(resbuf), "%d", res.val);
2198 urasm_exprval_clear(&res);
2199 return resbuf;
2203 /* evaluate next numeric expression in input string */
2204 /* there shoild be no other expressions in the string */
2205 /* returns expression value */
2206 static int32_t getOneExprArg (int *defined, int *addr) {
2207 int32_t res = getExprArg(defined, addr);
2208 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
2209 return res;
2213 /* is next expression a string literal? */
2214 UR_FORCE_INLINE int isStrArg (void) {
2215 const char *s = strSkipSpaces(currLine);
2216 return (s[0] == '"' || s[0] == '\'');
2220 /* check of we reached end of operator */
2221 UR_FORCE_INLINE int isOperatorEnd (void) {
2222 const char *s = strSkipSpaces(currLine);
2223 return (s[0] == 0 || s[0] == ':');
2227 /* check of we reached end of operator */
2228 UR_FORCE_INLINE int isLineEnd (void) {
2229 const char *s = strSkipSpaces(currLine);
2230 return (s[0] == 0 || s[0] == ';');
2234 /* parse string argument from input string */
2235 /* returns parsed string */
2236 static char *getStrArg (int *lenp) {
2237 char *res, qCh;
2238 char *a = strSkipSpaces(currLine);
2239 qCh = *a++;
2240 if (qCh != '"' && qCh != '\'') fatal("string expected");
2241 res = parseStr(&a, qCh, lenp);
2242 if (*a) {
2243 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
2244 memmove(currLine, a, strlen(a)+1);
2245 } else {
2246 currLine[0] = '\0';
2248 return res;
2252 /* parse string argument from input string; allows math after the string */
2253 /* returns parsed string */
2254 /* returns NULL and math expr in `lenp` */
2255 static char *getStrExprArg (int *lenp, int isWord) {
2256 char *a = strSkipSpaces(currLine);
2257 const char qCh = *a++;
2258 if (qCh != '"' && qCh != '\'') fatal("string expected");
2259 char *res = parseStr(&a, qCh, lenp);
2260 if (!res[0]) fatal("invalid empty string");
2261 if (*a) {
2262 if (a[0] != ',' && a[0] != ':' && strlen(res) <= 2) {
2263 memmove(currLine, a, strlen(a)+1);
2264 // "ab" -- 'a' will be in low (first) byte
2265 // 'ab' -- 'a' will be in high (second) byte
2266 int32_t sval;
2267 if (res[1]) {
2268 // two-byte
2269 if (qCh == '"') {
2270 sval = (uint8_t)(res[0]&0xffU)|(((uint8_t)(res[1]&0xffU))<<8);
2271 } else {
2272 sval = (uint8_t)(res[1]&0xffU)|(((uint8_t)(res[0]&0xffU))<<8);
2274 } else {
2275 // one-byte
2276 sval = (uint8_t)(res[0]&0xffU);
2278 //fprintf(stderr, "SMATH:000: str=<%s> (sval=%d); <%s>\n", res, sval, currLine);
2279 // parse math
2280 int defined = 0, fixuptype = UR_FIXUP_NONE;
2281 int32_t xadd = getExprArg(&defined, &fixuptype);
2282 //fprintf(stderr, "SMATH:001: str=<%s> (sval=%d; xadd=%d); <%s>\n", res, sval, xadd, currLine);
2283 if (pass > 0 && !defined) fatal("undefined operand");
2284 sval += xadd;
2285 if (currLine[0] && currLine[0] != ',' && currLine[0] != ':') fatal("bad string expression");
2286 res = NULL;
2287 if (lenp) *lenp = sval;
2288 } else {
2289 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
2290 memmove(currLine, a, strlen(a)+1);
2292 } else {
2293 currLine[0] = '\0';
2295 return res;
2299 /* get identifier (and lowercase it) */
2300 static char *getOneIdArgLo (void) {
2301 static char res[MAX_LINE_SIZE+128];
2302 char *p;
2303 char *a = strSkipSpaces(currLine);
2304 memset(res, 0, sizeof(res));
2305 if (!a[0]) fatal("identifier expected");
2306 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
2307 for (; p > res && isSpace(p[-1]); --p) {}
2308 *p = '\0';
2309 if (p-res > 120) fatal("identifier too long: %s", res);
2310 while (*a && isSpace(*a)) ++a;
2311 if (*a) {
2312 memmove(currLine, a, strlen(a)+1);
2313 if (currLine[0] == ';') currLine[0] = 0;
2314 if (currLine[0]) fatal("extra arguments");
2315 } else {
2316 currLine[0] = '\0';
2318 if (!res[0]) fatal("identifier expected");
2319 for (char *t = res; *t; ++t) *t = toLower(*t);
2320 return res;
2324 /* get label argument */
2325 static char *getLabelArg (int checkdelim) {
2326 static char res[MAX_LINE_SIZE+128];
2327 char *p;
2328 char *a = strSkipSpaces(currLine);
2329 memset(res, 0, sizeof(res));
2330 if (!a[0]) fatal("label expected");
2331 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
2332 for (; p > res && isSpace(p[-1]); --p) {}
2333 *p = '\0';
2334 if (p-res > 120) fatal("label name too long: %s", res);
2335 while (*a && isSpace(*a)) ++a;
2336 if (*a) {
2337 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
2338 memmove(currLine, a, strlen(a)+1);
2339 } else {
2340 currLine[0] = '\0';
2342 if (!res[0]) fatal("label expected");
2343 return res;
2347 /* get label argument, and ensure that it is the last one */
2348 static char *getOneLabelArg (void) {
2349 char *res = getLabelArg(1);
2350 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
2351 return res;
2355 /* returns ',' or 0 */
2356 static char eatComma (void) {
2357 char *a = strSkipSpaces(currLine);
2358 if (!a[0]) { currLine[0] = '\0'; return 0; }
2359 if (a[0] == ':') return 0;
2360 if (a[0] != ',') fatal("invalid expression: ',' expected");
2361 for (++a; *a && isSpace(*a); ++a) {}
2362 if (!a[0]) { currLine[0] = '\0'; return 0; }
2363 memmove(currLine, a, strlen(a)+1);
2364 return ',';
2368 static int checkDelim (char dm) {
2369 char *a = strSkipSpaces(currLine);
2370 if (a[0] != dm) return 0;
2371 for (++a; *a && isSpace(*a); ++a) {}
2372 if (!a[0]) { currLine[0] = '\0'; return 1; }
2373 memmove(currLine, a, strlen(a)+1);
2374 return 1;
2378 static void matchDelim (char dm) {
2379 if (!checkDelim(dm)) fatal("invalid expression: '%c' expected", dm);
2383 static int checkIdentCI (const char *ident) {
2384 ur_assert(ident && ident[0]);
2385 char *a = strSkipSpaces(currLine);
2386 while (*a && *ident) {
2387 char c0 = *a++; if (c0 >= 'A' && c0 <= 'Z') c0 = c0 - 'A' + 'a';
2388 char c1 = *ident++; if (c1 >= 'A' && c1 <= 'Z') c1 = c1 - 'A' + 'a';
2389 if (c0 != c1) return 0;
2391 if (ident[0] != 0) return 0;
2392 if (*a == '_' || isAlphaDigit(*a)) return 0;
2393 while (*a && isSpace(*a)) a += 1;
2394 if (*a) {
2395 memmove(currLine, a, strlen(a)+1);
2396 } else {
2397 currLine[0] = '\0';
2399 return 1;
2403 ///////////////////////////////////////////////////////////////////////////////
2404 // label processor
2406 static MAYBE_UNUSED void removeSpacesAndColons (void) {
2407 char *ep = strSkipSpacesColons(currLine);
2408 memmove(currLine, ep, strlen(ep)+1);
2412 static void checkExprEnd (void) {
2413 char *ep = strSkipSpaces(currLine);
2414 memmove(currLine, ep, strlen(ep)+1);
2415 if (currLine[0] && currLine[0] != ':') fatal("end of expression expected");
2419 static void checkOperatorEnd (void) {
2420 char *ep = strSkipSpaces(currLine);
2421 memmove(currLine, ep, strlen(ep)+1);
2422 if (currLine[0]) fatal("end of operator expected");
2426 /* remove label from curLine */
2427 static void removeLabel (void) {
2428 char *ep = currLine;
2429 if (ep[0] && !isSpace(ep[0])) for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {} // skip text
2430 // skip spaces and colons
2431 ep = strSkipSpacesColons(ep);
2432 memmove(currLine, ep, strlen(ep)+1);
2436 static int labelDoEQU (const char *lblname, const char *value) {
2437 static char n2[256];
2438 UrLabelInfo *lbl;
2440 if (value == NULL || lblname == NULL || !lblname[0] || strlen(lblname) > 255 || strlen(value) >= MAX_LINE_SIZE) return -1;
2441 memset(n2, 0, sizeof(n2));
2442 strcpy(n2, lblname);
2443 if (!urasm_is_valid_name(n2)) return -1; // this is not a valid label, get out of here
2444 // check if this can be an instruction
2445 lbl = urAddLabel(lblname);
2446 if (!lbl->refFile) {
2447 lbl->refLine = 0;
2448 lbl->refFile = strdup("artificially-defined-label");
2450 //fprintf(stderr, "labelDoEQU: <%s>=<%s>\n", lblname, value);
2451 strcpy(currLine, value);
2453 int defined = 1, addr = UR_FIXUP_NONE;
2454 int32_t res = getOneExprArg(&defined, &addr);
2455 lbl->type = LBL_TYPE_EQU;
2456 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2457 if (defined) {
2458 lbl->value = res;
2459 lbl->known = 1;
2460 } else {
2461 return -1; //fatal("can't calculate label %s", lbl->name);
2464 return 0;
2468 static void processLabel (void) {
2469 char *argstart;
2470 char *ep, *nn;
2471 static char n2[256];
2472 UrLabelInfo *lbl;
2473 int noLocAff = 0, doEQU = 0, isTemp = 0;
2474 //fprintf(stderr, "LINE %5d: <%s> from <%s>\n", (currSrcLine ? currSrcLine->lineNo : 0), currLine, (currSrcLine ? currSrcLine->fname : "<nowhere>"));
2475 memset(n2, 0, sizeof(n2));
2477 currSeenLabel[0] = 0;
2479 if (!currLine[0] || isSpace(currLine[0]) || currLine[0] == ':') {
2480 // this may be " id = smth" or " id equ smth"
2481 ep = currLine;
2482 // skip spaces
2483 while (isSpace(*ep)) ++ep;
2484 if (ep[0] == ':' || !ep[0] || (!isAlpha(ep[0]) && ep[0] != '_' && ep[0] != '.' && ep[0] != '@')) {
2485 removeLabel(); // removeLabel() removes any spaces, etc
2486 return;
2488 // this looks like a label; check for '=' or 'equ'
2489 while (isAlphaDigit(ep[0]) || ep[0] == '_' || ep[0] == '.' || ep[0] == '@') ++ep;
2490 nn = ep;
2491 // skip trailing spaces
2492 while (isSpace(*ep)) ++ep;
2493 if (ep[0] == '=') {
2494 doEQU = 0;
2495 argstart = ++ep;
2496 } else if (isSpace(*nn)) {
2497 doEQU = 1;
2498 argstart = strIsCommand("EQU", ep);
2499 } else {
2500 argstart = NULL;
2502 if (!argstart) {
2503 removeLabel(); // removeLabel() removes any spaces, etc
2504 return;
2506 // remove leading spaces from name
2507 // copy label
2508 ep = currLine;
2509 // skip spaces
2510 while (isSpace(*ep)) ++ep;
2511 if (ep >= nn) fatal("internal compiler error");
2512 if (nn-ep > 120) fatal("label too long");
2513 memset(n2, 0, sizeof(n2));
2514 memmove(n2, ep, nn-ep);
2515 if (urFindOp(n2) || !urasm_is_valid_name(n2)) {
2516 //fatal("invalid label name");
2517 removeLabel(); // removeLabel() removes any spaces, etc
2518 return;
2520 // remove label name
2521 memmove(currLine, argstart, strlen(argstart)+1);
2522 // append label
2523 if (n2[0] == '@' && n2[1] == '@' && !n2[2]) {
2524 // unnamed back/forward
2525 fatal("temporary label should end with ':'");
2527 noLocAff = 1;
2528 lbl = addTempLabel();
2530 } else {
2531 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
2532 lbl = findAddLabel(n2);
2534 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2535 if (doEQU && pass == 0 && lbl->type != LBL_TYPE_UNKNOWN) {
2536 fatal("duplicate label '%s'", lbl->name);
2538 int defined = 1, addr = UR_FIXUP_NONE;
2539 int32_t res = getOneExprArg(&defined, &addr);
2540 lbl->type = (doEQU ? LBL_TYPE_EQU : LBL_TYPE_ASS);
2541 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2542 if (defined) {
2543 lbl->value = res;
2544 lbl->known = 1;
2545 } else {
2546 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
2548 currLine[0] = '\0';
2549 return;
2552 // collect label
2553 for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {}
2554 if (ep-currLine > 120) fatal("label too long");
2556 // copy label
2557 memset(n2, 0, sizeof(n2));
2558 memmove(n2, currLine, ep-currLine);
2559 if (urFindOp(n2)) {
2560 ep = strSkipSpaces(ep);
2561 if (*ep != ':') return; // this must be an instruction, process it
2563 if (!urasm_is_valid_name(n2)) return; // this is not a valid label, get out of here
2565 // check for macro
2566 if (findMacro(n2)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
2568 // check if this can be instruction
2569 //ep = strSkipSpaces(ep);
2570 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
2571 // ok, we got a good label
2572 removeLabel();
2573 if (n2[0] == '@' && n2[1] == '@' && !n2[2]) {
2574 // unnamed back/forward
2575 noLocAff = 1; isTemp = 1;
2576 lbl = addTempLabel();
2577 } else {
2578 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
2579 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (currSrcLine ? currSrcLine->lineNo : 0));*/
2580 lbl = findAddLabel(n2);
2581 //printf("new: [%s]\n", lbl->name);
2584 // get command name
2585 if (currLine[0] == '=') {
2586 if (isTemp) fatal("cannot assign values to temp labels");
2587 doEQU = 0;
2588 argstart = currLine+1;
2589 } else {
2590 doEQU = 1;
2591 argstart = strIsCommand("EQU", currLine);
2593 if (!argstart || doEQU) {
2594 if (pass == 0 && !isTemp && lbl->type != LBL_TYPE_UNKNOWN) {
2595 fatal("duplicate label '%s'", lbl->name);
2599 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2600 if (argstart) {
2601 if (isTemp) fatal("cannot assign values to temp labels");
2602 // do '=' or 'EQU'
2603 memmove(currLine, argstart, strlen(argstart)+1);
2604 if (!doEQU) {
2605 if (lbl->type != LBL_TYPE_UNKNOWN && lbl->type != LBL_TYPE_ASS) {
2606 fatal("duplicate label '%s'", lbl->name);
2609 int defined = 1, addr = UR_FIXUP_NONE;
2610 int32_t res = getOneExprArg(&defined, &addr);
2611 lbl->type = (doEQU ? LBL_TYPE_EQU : LBL_TYPE_ASS);
2612 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2613 if (defined) {
2614 lbl->value = res;
2615 lbl->known = 1;
2616 } else {
2617 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
2619 currLine[0] = '\0';
2620 return;
2623 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
2624 // code label
2625 // update last seen global
2626 if (!isTemp) {
2627 if (lbl->name[0] != '{' && lbl->name[0] != '.' && !noLocAff) {
2628 //fprintf(stderr, "**: LASTGLOB: <%s>\n", lbl->name);
2629 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
2630 lastSeenGlobalLabel = strdup(lbl->name);
2632 lbl->type = LBL_TYPE_CODE;
2633 lbl->value = disp;
2634 lbl->known = 1;
2635 lbl->fixuptype = UR_FIXUP_WORD;
2637 snprintf(currSeenLabel, sizeof(currSeenLabel), "%s", n2);
2638 } else if (lbl->type != LBL_TYPE_UNKNOWN) {
2639 ur_assert(lbl->type == LBL_TYPE_CODE);
2640 ur_assert(lbl->value == disp);
2641 lbl->tempFwd = 0;
2642 } else {
2643 //fprintf(stderr, "FIXTEMP: %s\n", lbl->name);
2644 lbl->type = LBL_TYPE_CODE;
2645 lbl->value = disp;
2646 lbl->known = 1;
2647 lbl->fixuptype = UR_FIXUP_WORD;
2648 ur_assert(lbl->tempIdx != 0);
2649 lbl->tempFwd = 0;
2654 ///////////////////////////////////////////////////////////////////////////////
2655 // instruction finder (in source)
2657 /* array ends with NULL */
2658 /* returns line or NULL */
2659 /* iidx will be set to found instruction number */
2660 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
2661 if (iidx) *iidx = -1;
2662 for (SourceLine *cur = currSrcLine; cur; cur = cur->next) {
2663 va_list ap;
2664 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
2665 va_start(ap, iidx);
2666 for (int f = 0; ;++f) {
2667 const char *name = va_arg(ap, const char *);
2669 if (!name) break;
2670 if (strIsCommand(name, cur->line)) {
2671 va_end(ap);
2672 if (iidx) *iidx = f;
2673 return cur;
2676 va_end(ap);
2678 return NULL;
2682 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
2683 return findNextInstructionFromList(NULL, name, NULL);
2687 ///////////////////////////////////////////////////////////////////////////////
2688 // writers
2690 ///////////////////////////////////////////////////////////////////////////////
2691 static void writeFixups (void) {
2692 void writeFixupList (FILE *fo, int cnt, const char *lbl, int (*chk)(const FixupItem *)) {
2693 if (cnt > 0) {
2694 int prevaddr = 0;
2695 fprintf(fo, "%s:\n", lbl);
2696 if (optFixupType == 1) fprintf(fo, " dw %d ; count\n", cnt);
2697 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2698 if (chk(fx)) {
2699 fprintf(fo, " dw #%04X\n", fx->opaddr-prevaddr);
2700 if (optFixupType == 2) {
2701 prevaddr = fx->opaddr;
2705 if (optFixupType == 2 || optFixupType == 3) fprintf(fo, " dw 0 ; end marker\n");
2709 if (optFixupType == 0) {
2710 /* simple text file */
2711 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.txt");
2712 printf("writing fixups to '%s'...\n", fname);
2713 FILE *fo = fopen(fname, "w");
2714 free(fname);
2715 if (fo == NULL) fatal("can't write fixup file");
2716 fprintf(fo, "; addr dadr sz\n");
2717 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2718 static const char type[4] = "NWLH";
2719 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
2721 fclose(fo);
2722 } else {
2723 /* various asm formats */
2724 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.zas");
2725 printf("writing fixups to '%s'...\n", fname);
2726 FILE *fo = fopen(fname, "w");
2727 int cntw = 0, cntwl = 0, cntwh = 0, cntbl = 0, cntbh = 0;
2728 free(fname);
2729 if (fo == NULL) fatal("can't write fixup file");
2730 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2731 if (fx->fixuptype == UR_FIXUP_WORD) { ++cntw; continue; }
2732 switch (fx->fixuptype) {
2733 case UR_FIXUP_LOBYTE: if (fx->size == 2) ++cntwl; else ++cntbl; break;
2734 case UR_FIXUP_HIBYTE: if (fx->size == 2) ++cntwh; else ++cntbh; break;
2737 writeFixupList(fo, cntw, "fixup_table_w", lambda(int, (const FixupItem *fx) {
2738 return (fx->fixuptype == UR_FIXUP_WORD);
2739 }));
2740 writeFixupList(fo, cntwl, "fixup_table_wl", lambda(int, (const FixupItem *fx) {
2741 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 2);
2742 }));
2743 writeFixupList(fo, cntwh, "fixup_table_wh", lambda(int, (const FixupItem *fx) {
2744 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 2);
2745 }));
2746 writeFixupList(fo, cntbl, "fixup_table_bl", lambda(int, (const FixupItem *fx) {
2747 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 1);
2748 }));
2749 writeFixupList(fo, cntbh, "fixup_table_bh", lambda(int, (const FixupItem *fx) {
2750 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 1);
2751 }));
2752 fclose(fo);
2757 ///////////////////////////////////////////////////////////////////////////////
2758 /* return 'found' flag */
2759 static int findChunkFrom (int addr, int *start, int *len) {
2760 if (addr < 0) addr = 0;
2761 for (; addr <= 65535 && !memused[addr]; ++addr) {}
2762 if (addr > 65535) return 0;
2763 *start = addr;
2764 for (; addr <= 65535 && memused[addr]; ++addr) {}
2765 *len = addr-(*start);
2766 return 1;
2770 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
2771 for (; buflen >= 2; buflen -= 2) {
2772 int cnt = *buf++;
2773 uint8_t byte = *buf++;
2775 if (cnt == 0) {
2776 // copy
2777 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
2778 if (!memused[addr]) putByte(addr, *buf);
2779 if (addr == 0xffff) return;
2781 } else {
2782 // fill
2783 for (; cnt > 0; --cnt, ++addr) {
2784 if (!memused[addr]) putByte(addr, byte);
2785 if (addr == 0xffff) return;
2792 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
2793 if (fwrite(&b, 1, 1, fo) != 1) return -1;
2794 if (bxor) *bxor = (*bxor)^b;
2795 return 0;
2799 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
2800 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
2801 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
2802 return 0;
2806 ///////////////////////////////////////////////////////////////////////////////
2807 // .sna
2809 static int saveSna (const char *fname, int as48) {
2810 char *fn;// = malloc(strlen(fname)+16);
2811 uint8_t regs[27];
2812 FILE *fo;
2813 char abuf[32];
2815 //fn = strprintf("%s/%s.sna", optOutputDir, fname);
2816 fn = buildOutputFileName(fname, "sna");
2817 fo = fopen(fn, "wb");
2818 free(fn);
2819 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
2820 printf("out: %s.sna\n", fname);
2821 memmove(regs, ursna48, 27);
2822 #if 0
2823 if (optRunSNA) {
2824 /* push new address */
2825 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
2826 sp -= 2;
2827 putByte(sp, ent&0xFFU);
2828 putByte(sp+1, (ent>>8)&0xFFU);
2829 regs[23] = sp&0xFFU;
2830 regs[24] = (sp>>8)&0xFFU;
2832 fwrite(regs, 27, 1, fo);
2833 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
2834 fwrite(memory+16384, 49152, 1, fo);
2835 #endif
2837 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
2839 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
2840 //fprintf(stderr, "%d\n", memused[sp]);
2841 if (memused[sp] || memused[(sp+1)&0xffff]) {
2842 fprintf(stderr, "FATAL: can't save snapshot!\n");
2843 goto error;
2846 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
2847 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
2848 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
2850 sprintf(abuf, "%05d", clrAddr);
2851 memcpy(memory+23762, abuf, 5);
2852 sprintf(abuf, "%05d", ent);
2853 memcpy(memory+23773, abuf, 5);
2855 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
2857 if (!as48) {
2858 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
2860 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
2861 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
2862 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
2863 // write pages
2864 memset(memory, 0, 16384);
2865 for (int f = 1; f < 8; ++f) {
2866 if (f != 2 && f != 5) {
2867 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
2872 fclose(fo);
2873 return 0;
2874 error:
2875 fclose(fo);
2876 unlink(fname);
2877 return -1;
2881 ///////////////////////////////////////////////////////////////////////////////
2882 // bin chunks
2884 static void saveRaw (const char *basename) {
2885 char *fname = NULL;// = malloc(strlen(basename)+16);
2886 int start = 0, len;
2887 FILE *fo;
2889 while (findChunkFrom(start, &start, &len)) {
2890 if (fname != NULL) free(fname);
2891 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, (optTapExt ? "tap" : "bin"));
2892 fo = fopen(fname, "wb");
2893 if (!fo) {
2894 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
2895 } else {
2896 printf("out: %s\n", fname);
2897 if (fwrite(memory+start, len, 1, fo) != 1) {
2898 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2899 fclose(fo);
2900 unlink(fname);
2901 } else {
2902 fclose(fo);
2905 start += len;
2907 if (fname != NULL) free(fname);
2911 ///////////////////////////////////////////////////////////////////////////////
2912 // HoBeta files
2914 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
2915 char name[8];
2916 char type;
2917 uint16_t start;
2918 uint16_t len;
2919 uint8_t zero; // always zero
2920 uint8_t secLen; // length in sectors
2921 uint16_t checksum;
2922 } HOBHeader;
2925 static uint16_t calcHobSum (const void *hdr) {
2926 const uint8_t *buf = (const uint8_t *)hdr;
2927 uint16_t res = 0;
2929 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
2930 return res;
2934 static void saveHob (const char *basename) {
2935 char *fname = NULL;//malloc(strlen(basename)+16);
2936 int start = 0, len;
2937 HOBHeader hdr;
2938 FILE *fo;
2940 while (findChunkFrom(start, &start, &len)) {
2941 if (fname != NULL) free(fname);
2942 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, "$C");
2943 fo = fopen(fname, "wb");
2944 if (!fo) {
2945 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
2946 start += len;
2947 } else {
2948 char tmpbuf[sizeof(hdr.name)*2];
2949 printf("out: %s\n", fname);
2950 memset(&hdr, 0, sizeof(hdr));
2951 memset(&hdr.name, 32, 8);
2952 snprintf(tmpbuf, sizeof(tmpbuf), "%c%04X", basename[0], start);
2953 while (strlen(tmpbuf) < sizeof(hdr.name)) strcat(tmpbuf, " "); /* sorry! */
2954 memcpy(hdr.name, tmpbuf, sizeof(hdr.name));
2955 hdr.type = 'C';
2956 hdr.start = start;
2957 hdr.len = len;
2958 hdr.secLen = (len+255)/256;
2959 hdr.checksum = calcHobSum(&hdr);
2960 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
2961 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2962 fclose(fo);
2963 unlink(fname);
2964 goto quit;
2966 if (fwrite(memory+start, len, 1, fo) != 1) {
2967 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2968 fclose(fo);
2969 unlink(fname);
2970 goto quit;
2972 start += len;
2973 while (len%256) {
2974 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
2975 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2976 fclose(fo);
2977 unlink(fname);
2978 goto quit;
2980 ++len;
2981 //fprintf(stderr, ":%d\n", len%256);
2983 fclose(fo);
2986 quit:
2987 if (fname != NULL) free(fname);
2991 // ////////////////////////////////////////////////////////////////////////// //
2992 typedef struct {
2993 uint8_t fcount;
2994 uint8_t dir[14*256];
2995 uint32_t dirpos;
2996 uint32_t currfstartpos;
2997 uint8_t *data;
2998 size_t datapos;
2999 size_t datasize;
3000 } SCLFile;
3003 static const uint8_t monoldr_code[169] = {
3004 0x00,0x00,0xff,0xf0,0xff,0xf0,0xff,0xf0,0xff,0xf3,0x01,0xfd,0x7f,0x3e,0x10,0xed,
3005 0x79,0xaf,0x21,0x00,0x58,0x11,0x01,0x58,0x01,0xff,0x02,0xfb,0x76,0xf3,0xd3,0xfe,
3006 0x36,0x00,0xed,0xb0,0x21,0x00,0x40,0x11,0x01,0x40,0x01,0x00,0x18,0x77,0xed,0xb0,
3007 0xfb,0x76,0xf3,0x3b,0x3b,0xe1,0xe5,0x11,0xce,0xff,0x19,0x7e,0x23,0x5e,0x23,0x56,
3008 0x23,0xc1,0xe5,0xd5,0x69,0x60,0x01,0x35,0x00,0x09,0x4f,0x87,0x81,0x01,0x43,0x00,
3009 0x81,0x4f,0x3e,0x00,0x88,0x47,0xed,0xb0,0xd1,0xe1,0xd5,0x01,0xff,0x03,0x13,0xed,
3010 0xa0,0xed,0xa0,0x13,0x10,0xf8,0xc9,0x31,0xa5,0xa5,0xfb,0x21,0x5a,0x5a,0xe5,0x21,
3011 0x9a,0x02,0xe5,0x76,0x3b,0x3b,0xe1,0x01,0x35,0x00,0x09,0x0e,0x00,0x7e,0xb7,0x28,
3012 0x23,0x23,0x5e,0x23,0x56,0x23,0xe5,0xeb,0x06,0x08,0xb8,0x30,0x01,0x47,0x90,0xf5,
3013 0xc5,0xe5,0xed,0x5b,0xf4,0x5c,0x0e,0x05,0xcd,0x13,0x3d,0xe1,0xc1,0x09,0xf1,0x20,
3014 0xe7,0xe1,0x18,0xd9,0xe1,0xd1,0xf9,0xeb,0xe9,
3018 static void sclInit (SCLFile *scl) {
3019 memset(scl, 0, sizeof(*scl));
3020 scl->datasize = 2*80*16*256; /* maximum disk size */
3021 scl->data = malloc(scl->datasize);
3022 memset(scl->data, 0, scl->datasize);
3023 scl->currfstartpos = 0xffffffffu;
3027 static void sclClear (SCLFile *scl) {
3028 if (!scl) return;
3029 if (scl->data) free(scl->data);
3030 memset(scl, 0, sizeof(*scl));
3031 scl->currfstartpos = 0xffffffffu;
3035 static void sclStartFile (SCLFile *scl, const char *name, char type, uint16_t v0, uint16_t v1) {
3036 if (scl->fcount == 255) {
3037 fprintf(stderr, "FATAL: too many files in SCL!\n");
3038 exit(1);
3040 if (scl->currfstartpos != 0xffffffffu) {
3041 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
3042 exit(1);
3044 int nlen = 0;
3045 while (nlen < 8 && name && name[nlen]) scl->dir[scl->dirpos++] = name[nlen++];
3046 while (nlen < 8) { scl->dir[scl->dirpos++] = ' '; ++nlen; }
3047 scl->dir[scl->dirpos++] = type;
3048 scl->dir[scl->dirpos++] = v0&0xff;
3049 scl->dir[scl->dirpos++] = (v0>>8)&0xff;
3050 scl->dir[scl->dirpos++] = v1&0xff;
3051 scl->dir[scl->dirpos++] = (v1>>8)&0xff;
3052 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
3053 scl->currfstartpos = scl->datapos;
3057 static void sclEndFile (SCLFile *scl) {
3058 if (scl->currfstartpos == 0xffffffffu) {
3059 fprintf(stderr, "FATAL: last SCL file already finished!\n");
3060 exit(1);
3062 /* align to sector */
3063 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3064 const uint32_t fsz = scl->datapos-scl->currfstartpos;
3065 if (fsz > 255*256) {
3066 fprintf(stderr, "FATAL: SCL file too big!\n");
3067 exit(1);
3069 ur_assert((fsz&255) == 0);
3070 scl->dir[scl->dirpos++] = fsz/256; /* size in sectors */
3071 ur_assert(scl->dirpos%14 == 0);
3072 ++scl->fcount;
3073 scl->currfstartpos = 0xffffffffu;
3077 static void sclWriteDataForce (SCLFile *scl, const void *buf, size_t len) {
3078 if (!len) return;
3079 if (len > 255*256) {
3080 fprintf(stderr, "FATAL: SCL file too big!\n");
3081 exit(1);
3083 memcpy(scl->data+scl->datapos, buf, len);
3084 scl->datapos += len;
3088 static void sclWriteData (SCLFile *scl, const void *buf, size_t len) {
3089 if (scl->currfstartpos == 0xffffffffu) {
3090 fprintf(stderr, "FATAL: no open SCL file!\n");
3091 exit(1);
3093 sclWriteDataForce(scl, buf, len);
3097 static void sclWriteByte (SCLFile *scl, uint8_t b) {
3098 sclWriteData(scl, &b, 1);
3102 static void sclWriteWord (SCLFile *scl, uint16_t w) {
3103 uint8_t b = w&0xff;
3104 sclWriteData(scl, &b, 1);
3105 b = (w>>8)&0xff;
3106 sclWriteData(scl, &b, 1);
3110 static int sclFileWrite (FILE *fo, const void *buf, size_t count, uint32_t *checksum) {
3111 if (!count) return 0;
3112 const uint8_t *p = (const uint8_t *)buf;
3113 if (checksum) for (size_t n = count; n--; ++p) *checksum += (uint32_t)(*p);
3114 if (fwrite(buf, count, 1, fo) != 1) return -1;
3115 return 0;
3119 // <0: error; 0: ok
3120 static int sclSaveToFile (FILE *fo, SCLFile *scl) {
3121 if (scl->currfstartpos != 0xffffffffu) {
3122 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
3123 exit(1);
3125 const char *sign = "SINCLAIR";
3126 uint32_t checksum = 0;
3127 // header
3128 if (sclFileWrite(fo, sign, 8, &checksum) != 0) return -1;
3129 if (sclFileWrite(fo, &scl->fcount, 1, &checksum) != 0) return -1;
3130 // directory
3131 if (sclFileWrite(fo, scl->dir, scl->dirpos, &checksum) != 0) return -1;
3132 // data
3133 if (sclFileWrite(fo, scl->data, scl->datapos, &checksum) != 0) return -1;
3134 // write checksum
3135 for (unsigned f = 0; f < 4; ++f) {
3136 const uint8_t b = (checksum>>(f*8))&0xff;
3137 if (fwrite(&b, 1, 1, fo) != 1) return -1;
3139 // done
3140 return 0;
3144 // ////////////////////////////////////////////////////////////////////////// //
3145 static void saveSCLCargador (SCLFile *scl) {
3146 static uint8_t cargador[16384]; // should be enough for everyone
3147 int linestart = -1;
3148 int linenum = 0;
3149 int start = 0, len, pos;
3151 void putStr (const char *s) {
3152 for (; *s; ++s) cargador[pos++] = *s;
3155 void putNum (int num) {
3156 char buf[64];
3157 sprintf(buf, "%d", num);
3158 putStr(buf);
3161 void startLine () {
3162 ++linenum;
3163 linestart = pos;
3164 // number
3165 cargador[pos++] = (linenum>>8)&0xff;
3166 cargador[pos++] = linenum&0xff;
3167 // size (will be fixed later)
3168 cargador[pos++] = 0;
3169 cargador[pos++] = 0;
3172 void flushLine () {
3173 if (linestart >= 0) {
3174 const int size = pos-linestart-4;
3175 cargador[linestart+2] = size&0xff;
3176 cargador[linestart+3] = (size>>8)&0xff;
3178 linestart = -1;
3181 char cname[16];
3183 pos = 0;
3184 startLine();
3185 // CLEAR VAL "xxx"
3186 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
3188 // load blocks
3189 int cont = 1;
3190 while (findChunkFrom(start, &start, &len)) {
3191 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3192 if (cont) { putStr(":"); cont = 0; }
3193 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3194 // generate chunk name
3195 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
3196 putStr(cname);
3197 //" CODE
3198 putStr("\"\xaf\r");
3199 flushLine();
3200 startLine();
3201 start += len;
3204 // load code files
3205 for (OutDataFile *off = datafiles; off; off = off->next) {
3206 if (off->type == 1) continue; // skip +3DOS bootsector
3207 if (off->type != 'C') continue;
3208 if (!off->hasstart) continue;
3209 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3210 if (cont) { putStr(":"); cont = 0; }
3211 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3212 // generate chunk name
3213 snprintf(cname, sizeof(cname), "%s", off->name);
3214 char *dotpos = strchr(cname, '.');
3215 char type = 'C';
3216 if (dotpos) { type = toUpper(dotpos[1]); *dotpos = 0; }
3217 if (type < 'A' || type > 'Z') type = 'C';
3218 for (char *s = cname; *s; ++s) *s = toUpper(*s);
3219 putStr(cname);
3220 //" CODE
3221 putStr("\"\xaf\r");
3222 flushLine();
3223 startLine();
3226 // and run
3227 if (cont) { putStr(":"); cont = 0; }
3228 // RANDOMIZE USR VAL "xxx"
3229 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"");
3230 putStr("\r");
3231 flushLine();
3233 //putWord(0xaa80);
3234 //putWord(1); // autostart line
3236 // write to SCL
3237 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
3238 sclWriteData(scl, cargador, (size_t)pos);
3239 sclWriteByte(scl, 0x80);
3240 sclWriteByte(scl, 0xaa);
3241 sclWriteWord(scl, 1);
3242 sclEndFile(scl);
3246 static void saveSCLCargadorMono (SCLFile *scl) {
3247 static uint8_t cargador[16384]; // should be enough for everyone
3248 int linestart = -1;
3249 int linenum = 0;
3250 int start = 0, len, pos;
3252 void putDB (uint8_t v) {
3253 cargador[pos++] = v;
3256 void putDW (uint16_t v) {
3257 cargador[pos++] = v&0xff;
3258 cargador[pos++] = (v>>8)&0xff;
3261 void putStr (const char *s) {
3262 for (; *s; ++s) cargador[pos++] = *s;
3265 void putNum (int num) {
3266 char buf[64];
3267 sprintf(buf, "%d", num);
3268 putStr(buf);
3271 void startLine () {
3272 linestart = pos;
3273 // number
3274 cargador[pos++] = (linenum>>8)&0xff;
3275 cargador[pos++] = linenum&0xff;
3276 // size (will be fixed later)
3277 cargador[pos++] = 0;
3278 cargador[pos++] = 0;
3279 linenum++;
3282 void flushLine () {
3283 if (linestart >= 0) {
3284 const int size = pos-linestart-4;
3285 cargador[linestart+2] = size&0xff;
3286 cargador[linestart+3] = (size>>8)&0xff;
3288 linestart = -1;
3291 pos = 0;
3292 startLine();
3294 // 0 REM code
3295 putStr("\xea");
3296 // monoloader parameters:
3297 // dw ldr_copy_addr
3298 // dw ldr_sp
3299 // dw prg_start_addr
3300 // dw prg_sp
3301 // block count
3302 const int bcountpos = pos;
3303 putDB(0);
3304 putDW(0x4000); // to screen$
3305 putDW(0x4200); // stack
3306 putDW(ent); // program start address
3307 putDW(0x0000); // stack
3308 // copy monoloader
3309 for (uint32_t f = 9; f < (uint32_t)sizeof(monoldr_code); ++f) {
3310 putDB(monoldr_code[f]);
3312 // put block data
3313 while (findChunkFrom(start, &start, &len)) {
3314 if (len > 255*256) fatal("code chunk too big");
3315 // sector count
3316 putDB(len/256+(len%256 ? 1 : 0));
3317 // address
3318 putDW(start);
3319 start += len;
3320 // one more block
3321 ++cargador[bcountpos];
3323 // load code files
3324 for (OutDataFile *off = datafiles; off; off = off->next) {
3325 if (off->type == 1) continue; // skip +3DOS bootsector
3326 if (off->type != 'C') continue;
3327 if (!off->hasstart) continue;
3328 if (off->size > 255*256) fatal("data chunk too big");
3329 // sector count
3330 putDB(off->size/256+(off->size%256 ? 1 : 0));
3331 // address
3332 putDW(off->start);
3333 // one more block
3334 ++cargador[bcountpos];
3336 // no more blocks
3337 putDB(0);
3339 flushLine();
3340 startLine();
3342 // CLEAR VAL "xxx":RANDOMIZE USR PEEK VAL "23635"+VAL "256"*PEEK VAL "23636"+VAL "14"
3343 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\":");
3344 putStr("\xf9\xc0(\xbe\xb0\"23635\"+\xb0\"256\"*\xbe\xb0\"23636\"+\xb0\"14\")\r");
3345 flushLine();
3347 // write to SCL
3348 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
3349 sclWriteData(scl, cargador, (size_t)pos);
3350 sclWriteByte(scl, 0x80);
3351 sclWriteByte(scl, 0xaa);
3352 sclWriteWord(scl, 1);
3353 sclEndFile(scl);
3355 // put block data
3356 uint8_t ssz = 0;
3357 start = 0;
3358 while (findChunkFrom(start, &start, &len)) {
3359 ssz += len/256+(len%256 ? 1 : 0);
3360 sclWriteDataForce(scl, memory+start, len);
3361 /* align to sector */
3362 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3363 start += len;
3365 for (unsigned dphase = 0; dphase < 2; ++dphase) {
3366 // put code files
3367 for (OutDataFile *off = datafiles; off; off = off->next) {
3368 if (off->type == 1) continue; // skip +3DOS bootsector
3369 if (off->type != 'C') continue;
3370 if (dphase == 0) {
3371 if (!off->hasstart) continue;
3372 } else {
3373 if (off->hasstart) continue;
3375 if (off->size > 255*256) fatal("data chunk too big");
3376 ssz += off->size/256+(off->size%256 ? 1 : 0);
3377 sclWriteDataForce(scl, off->data, off->size);
3378 /* align to sector */
3379 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3383 // fix basic file sector size
3384 scl->dir[scl->dirpos-1] += ssz;
3388 static void saveSCL (const char *basename) {
3389 SCLFile scl;
3390 int fcount = 0;
3391 int start, len;
3392 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3393 char *fname = buildOutputFileName(basename, "scl");
3395 // count files
3396 start = 0;
3397 while (findChunkFrom(start, &start, &len)) {
3398 ++fcount;
3399 start += len;
3401 // count data files
3402 for (OutDataFile *off = datafiles; off; off = off->next) {
3403 if (off->type == 1) continue; // skip +3DOS bootsector
3404 if (off->type == 'C') ++fcount;
3407 if (fcount && optRunSCL) fcount += (optRunSCL > 0 ? 2 : 1); // +loader and boot
3408 if (fcount > 255) {
3409 fprintf(stderr, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname, fcount);
3410 exit(1);
3413 // create output file
3414 FILE *fo = fopen(fname, "wb");
3415 if (!fo) {
3416 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
3417 exit(1);
3420 // initialise SCL writer
3421 sclInit(&scl);
3422 // write loader
3423 if (fcount && optRunSCL) {
3424 // create simple boot
3425 if (optRunSCL > 0) {
3426 const uint8_t dasboot[] = {
3427 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"
3429 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
3430 sclWriteWord(&scl, 1); // line number
3431 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
3432 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
3433 // TR-DOS signature
3434 sclWriteByte(&scl, 0x80);
3435 sclWriteByte(&scl, 0xaa);
3436 // start line
3437 sclWriteWord(&scl, 0);
3438 sclEndFile(&scl);
3440 saveSCLCargador(&scl);
3443 // write chunks
3444 char cname[16];
3445 start = 0;
3446 while (findChunkFrom(start, &start, &len)) {
3447 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
3448 sclStartFile(&scl, cname, 'C', (unsigned)start, (unsigned)len);
3449 sclWriteData(&scl, memory+(unsigned)start, (unsigned)len);
3450 sclEndFile(&scl);
3451 start += len;
3454 // write data files
3455 for (OutDataFile *off = datafiles; off; off = off->next) {
3456 if (off->type == 1) continue; // skip +3DOS bootsector
3457 if (off->type != 'C') {
3458 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3459 continue;
3461 snprintf(cname, sizeof(cname), "%s", off->name);
3462 char *dotpos = strchr(cname, '.');
3463 char type = 'C';
3464 if (dotpos) { type = toUpper(dotpos[1]); *dotpos = 0; }
3465 if (type < 'A' || type > 'Z') type = 'C';
3466 for (char *s = cname; *s; ++s) *s = toUpper(*s);
3467 sclStartFile(&scl, cname, type, (unsigned)off->start, (unsigned)off->size);
3468 sclWriteData(&scl, off->data, (unsigned)off->size);
3469 sclEndFile(&scl);
3472 if (sclSaveToFile(fo, &scl) < 0) {
3473 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
3474 fclose(fo);
3475 unlink(fname);
3476 exit(1);
3478 sclClear(&scl);
3479 fclose(fo);
3480 printf("out: %s\n", fname);
3481 if (fname != NULL) free(fname);
3485 static void saveSCLMono (const char *basename) {
3486 SCLFile scl;
3487 int start, len;
3488 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3489 char *fname = buildOutputFileName(basename, "scl");
3491 // count total size in sectors
3492 int secsize = 1; // for loader
3493 start = 0;
3494 while (findChunkFrom(start, &start, &len)) {
3495 secsize += len/256+(len%256 ? 1 : 0);
3496 start += len;
3498 // count data files
3499 for (OutDataFile *off = datafiles; off; off = off->next) {
3500 if (off->type == 1) continue; // skip +3DOS bootsector
3501 if (off->type == 'C') {
3502 secsize += off->size/256+(off->size%256 ? 1 : 0);
3506 if (secsize > 254) {
3507 fprintf(stderr, "ERROR: can't write file '%s' (too many sectors: %d)!\n", fname, secsize);
3508 exit(1);
3511 // create output file
3512 FILE *fo = fopen(fname, "wb");
3513 if (!fo) {
3514 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
3515 exit(1);
3518 // initialise SCL writer
3519 sclInit(&scl);
3520 // write loader
3521 if (optRunSCL) {
3522 // create simple boot
3523 if (optRunSCL > 0) {
3524 const uint8_t dasboot[] = {
3525 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"
3527 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
3528 sclWriteWord(&scl, 1); // line number
3529 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
3530 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
3531 // TR-DOS signature
3532 sclWriteByte(&scl, 0x80);
3533 sclWriteByte(&scl, 0xaa);
3534 // start line
3535 sclWriteWord(&scl, 0);
3536 sclEndFile(&scl);
3539 saveSCLCargadorMono(&scl);
3541 if (sclSaveToFile(fo, &scl) < 0) {
3542 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
3543 fclose(fo);
3544 unlink(fname);
3545 exit(1);
3547 sclClear(&scl);
3548 fclose(fo);
3549 printf("out: %s\n", fname);
3550 if (fname != NULL) free(fname);
3554 // ////////////////////////////////////////////////////////////////////////// //
3555 int optRunDSK = 0;
3556 int optDskType = P3DSK_PCW_SS;
3559 static int saveDSKCargador (P3DiskInfo *p3d, int autorun) {
3560 static uint8_t cargador[16384]; // should be enough for everyone
3561 int linestart = -1;
3562 int linenum = 0;
3563 int start = 0, len, pos;
3565 void putStr (const char *s) {
3566 for (; *s; ++s) cargador[pos++] = *s;
3569 void putNum (int num) {
3570 char buf[64];
3571 sprintf(buf, "%d", num);
3572 putStr(buf);
3575 void startLine () {
3576 ++linenum;
3577 linestart = pos;
3578 // number
3579 cargador[pos++] = (linenum>>8)&0xff;
3580 cargador[pos++] = linenum&0xff;
3581 // size (will be fixed later)
3582 cargador[pos++] = 0;
3583 cargador[pos++] = 0;
3586 void flushLine () {
3587 if (linestart >= 0) {
3588 const int size = pos-linestart-4;
3589 cargador[linestart+2] = size&0xff;
3590 cargador[linestart+3] = (size>>8)&0xff;
3592 linestart = -1;
3595 char cname[16];
3597 pos = 0;
3598 startLine();
3599 // CLEAR VAL "xxx"
3600 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
3601 // load blocks
3602 while (findChunkFrom(start, &start, &len)) {
3603 // :LOAD "
3604 putStr(":\xef\"");
3605 // generate chunk name
3606 snprintf(cname, sizeof(cname), "C%04X.BIN", (unsigned)start);
3607 putStr(cname);
3608 // " CODE
3609 putStr("\"\xaf");
3610 start += len;
3612 putStr("\r");
3613 flushLine();
3614 startLine();
3615 // and run
3616 // :RANDOMIZE USR VAL "xxx"
3617 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
3618 flushLine();
3620 // write to DSK
3621 // build cp/m 8.3 name
3622 char pdfname[16];
3623 if (autorun) strcpy(pdfname, "DISK"); else strcpy(pdfname, "CARGADOR.BAS");
3625 int crerr = p3dskCreateFile(p3d, pdfname);
3626 if (crerr < 0) {
3627 if (crerr == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3628 if (crerr != FLPERR_FILEEXIST) { fprintf(stderr, "+3DOS disk error (creating)\n"); return -1; }
3629 p3dskDeleteFiles(p3d, pdfname);
3630 crerr = p3dskCreateFile(p3d, pdfname);
3631 if (crerr < 0) { fprintf(stderr, "+3DOS disk error (creating1)\n"); return -1; }
3634 P3DskFileInfo nfo;
3635 if (p3dskOpenFile(p3d, &nfo, pdfname) != FLPERR_OK) { fprintf(stderr, "cannot open created +3DOS file '%s'\n", pdfname); return -1; }
3637 int wrres = p3dskWriteFile(&nfo, cargador, 128, (int)pos);
3638 if (wrres == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3639 if (wrres == FLPERR_NOSPACE) { fprintf(stderr, "NO FREE SPACE (writing)!\n"); return -1; }
3640 if (wrres < 0) { fprintf(stderr, "+3DOS disk writing error (writing)\n"); return -1; }
3642 // write +3DOS header
3643 P3DskFileHeader hdr;
3644 memset(&hdr, 0, sizeof(P3DskFileHeader));
3645 hdr.valid = 1;
3646 hdr.issue = 1;
3647 hdr.version = 0;
3648 hdr.filesize = (uint32_t)(pos+128);
3649 hdr.bastype = 0; // basic
3650 hdr.baslength = (uint16_t)pos;
3651 hdr.basaddr = 1;
3652 hdr.basvarsofs = (uint16_t)pos;
3653 if (p3dskWriteFileHeader(&nfo, &hdr) != FLPERR_OK) { fprintf(stderr, "error writing +3DOS file header\n"); return -1; }
3655 return 0;
3659 static int saveDSKChunk (P3DiskInfo *p3d, const char *name, const void *data, int start, int len, int p3header) {
3660 // write to DSK
3661 // build cp/m 8.3 name
3662 char pdfname[13];
3663 if (p3dskNormaliseFileName(pdfname, name) != FLPERR_OK) { fprintf(stderr, "bad +3DOS file name '%s'\n", name); return -1; }
3665 for (char *s = pdfname; *s; ++s) if (*s >= 'a' && *s <= 'z') s[0] -= 32;
3667 int crerr = p3dskCreateFile(p3d, pdfname);
3668 if (crerr < 0) {
3669 if (crerr == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3670 if (crerr != FLPERR_FILEEXIST) { fprintf(stderr, "+3DOS disk error (creating)\n"); return -1; }
3671 p3dskDeleteFiles(p3d, pdfname);
3672 crerr = p3dskCreateFile(p3d, pdfname);
3673 if (crerr < 0) { fprintf(stderr, "+3DOS disk error (creating1)\n"); return -1; }
3676 P3DskFileInfo nfo;
3677 if (p3dskOpenFile(p3d, &nfo, pdfname) != FLPERR_OK) { fprintf(stderr, "cannot open created +3DOS file '%s'\n", pdfname); return -1; }
3679 int wrres = p3dskWriteFile(&nfo, data, (p3header ? 128 : 0), (int)len);
3680 if (wrres == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3681 if (wrres == FLPERR_NOSPACE) { fprintf(stderr, "NO FREE SPACE (writing)!\n"); return -1; }
3682 if (wrres < 0) { fprintf(stderr, "+3DOS disk writing error (writing)\n"); return -1; }
3684 // write +3DOS header
3685 if (p3header) {
3686 P3DskFileHeader hdr;
3687 memset(&hdr, 0, sizeof(P3DskFileHeader));
3688 hdr.valid = 1;
3689 hdr.issue = 1;
3690 hdr.version = 0;
3691 hdr.filesize = (uint32_t)(len+128);
3692 hdr.bastype = 3; // code
3693 hdr.baslength = (uint16_t)len;
3694 hdr.basaddr = (uint16_t)start;
3695 hdr.basvarsofs = 0;
3696 if (p3dskWriteFileHeader(&nfo, &hdr) != FLPERR_OK) { fprintf(stderr, "error writing +3DOS file header\n"); return -1; }
3699 return 0;
3703 static void saveDSK (const char *basename) {
3704 // create and format the floppy
3705 Floppy *flp = flpCreate(0);
3706 if (p3dskFormatDisk(flp, optDskType) != FLPERR_OK) {
3707 flpDestroy(flp);
3708 fprintf(stderr, "ERROR: cannot format +3DOS disk\n");
3709 return;
3712 P3DiskInfo p3d;
3713 p3d.flp = flp;
3714 if (p3dskDetectGeom(p3d.flp, &p3d.geom) < 0) {
3715 flpDestroy(flp);
3716 fprintf(stderr, "ERROR: not a +3DOS disk!\n");
3717 return;
3720 int hasbootsector = 0;
3721 for (OutDataFile *off = datafiles; off; off = off->next) {
3722 if (off->type == 1) { hasbootsector = 1; break; }
3725 int start = 0, len;
3726 int haschunks = findChunkFrom(start, &start, &len);
3728 if (!hasbootsector && haschunks) {
3729 if (saveDSKCargador(&p3d, optRunDSK) != 0) {
3730 flpDestroy(flp);
3731 return;
3735 // write chunks
3736 start = 0;
3737 while (findChunkFrom(start, &start, &len)) {
3738 char cname[16];
3739 snprintf(cname, sizeof(cname), "C%04X.BIN", (unsigned)start);
3740 if (saveDSKChunk(&p3d, cname, memory+(unsigned)start, start, len, 1) != 0) {
3741 flpDestroy(flp);
3742 return;
3744 start += len;
3747 // write data files
3748 for (OutDataFile *off = datafiles; off; off = off->next) {
3749 if (off->type == 1) {
3750 // +3DOS bootsector
3751 if (!p3d.geom.restracks) {
3752 fprintf(stderr, "no room for +3DOS bootsector\n");
3753 continue;
3755 uint8_t *bootsec = malloc(512);
3756 if (flpGetSectorData(p3d.flp, 0, p3d.geom.firstsector, bootsec, 512) != FLPERR_OK) {
3757 fprintf(stderr, "cannot read +3DOS bootsector\n");
3758 continue;
3760 size_t clen = off->size;
3761 if (clen > 512-16) clen = 512-16;
3762 if (clen) memcpy(bootsec+16, off->data, clen);
3763 if (flpPutSectorData(p3d.flp, 0, p3d.geom.firstsector, bootsec, 512) != FLPERR_OK) fatal("cannot write +3DOS bootsector");
3764 if (p3dskWriteBootableChecksum(&p3d) != FLPERR_OK) fatal("cannot make +3DOS disk bootable");
3765 printf("written +3DOS bootsector (%u bytes of code)\n", off->size);
3766 continue;
3769 if (off->type != 'C') {
3770 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3771 continue;
3773 char pdfname[13];
3774 if (p3dskNormaliseFileName(pdfname, off->name) != FLPERR_OK) {
3775 fprintf(stderr, "skipping data file with invalid name '%s'\n", off->name);
3776 continue;
3778 saveDSKChunk(&p3d, pdfname, off->data, off->start, off->size, off->hasstart);
3781 // write disk
3782 //char *fname = strprintf("%s/%s.dsk", optOutputDir, basename);
3783 char *fname = buildOutputFileName(basename, "dsk");
3784 FILE *fo = fopen(fname, "wb");
3785 if (!fo) { fprintf(stderr, "cannot create disk file '%s'\n", fname); free(fname); flpDestroy(flp); return; }
3786 if (dskSaveDSK(flp, fo) != FLPERR_OK) {
3787 unlink(fname);
3788 fprintf(stderr, "error writing disk file '%s'\n", fname);
3789 free(fname);
3790 flpDestroy(flp);
3791 return;
3793 fclose(fo);
3794 printf("out: %s\n", fname);
3795 free(fname);
3796 flpDestroy(flp);
3800 ///////////////////////////////////////////////////////////////////////////////
3801 // .dmb
3803 static int saveDMB (const char *fname) {
3804 char *fn;// = malloc(strlen(fname)+16);
3805 int start = 0, len;
3806 uint16_t pcnt = 0;
3807 FILE *fo;
3809 //fn = strprintf("%s/%s.dmb", optOutputDir, fname);
3810 fn = buildOutputFileName(fname, "dmb");
3811 fo = fopen(fn, "wb");
3812 free(fn);
3813 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
3815 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
3817 printf("out: %s.dmb\n", fname);
3818 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
3819 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
3820 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
3821 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
3823 start = 0;
3824 while (findChunkFrom(start, &start, &len)) {
3825 if (fWriteWord(fo, len, NULL) != 0) goto error;
3826 if (fWriteWord(fo, start, NULL) != 0) goto error;
3827 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
3828 start += len;
3830 fclose(fo);
3831 return 0;
3832 error:
3833 fclose(fo);
3834 unlink(fname);
3835 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
3836 return -1;
3840 ///////////////////////////////////////////////////////////////////////////////
3841 // .tap
3843 static char tapeLoaderName[16];
3846 static void saveTapCargador (FILE *fo) {
3847 // count blocks
3848 static uint8_t cargador[16384]; // should be enough for everyone
3849 int start = 0, len, pos, f;
3850 uint8_t bxor;
3853 void putStr (const char *s) {
3854 for (; *s; ++s) cargador[pos++] = *s;
3857 void putNum (int num) {
3858 char buf[64];
3859 sprintf(buf, "%d", num);
3860 putStr(buf);
3864 pos = 4;
3865 // number
3866 cargador[0] = 0; cargador[1] = 10;
3867 // size (will be fixed later)
3868 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
3869 // load blocks
3870 while (findChunkFrom(start, &start, &len)) {
3871 // :LOAD "" CODE
3872 putStr(":\xef\"\"\xaf");
3873 start += len;
3875 // additional code files
3876 for (OutDataFile *off = datafiles; off; off = off->next) {
3877 if (off->type == 1) continue; // skip +3DOS bootsector
3878 if (off->type != 'C' || off->size > 65533 || !off->hasstart) {
3879 continue;
3881 // :LOAD "" CODE
3882 putStr(":\xef\"\"\xaf");
3883 start += len;
3885 // and run
3886 // :RANDOMIZE USR VAL "xxx"
3887 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
3888 // patch len
3889 cargador[2] = (pos-4)&0xff;
3890 cargador[3] = ((pos-4)>>8)&0xff;
3891 // write header
3892 fWriteWord(fo, 19, NULL); // length of header
3893 bxor = 0;
3894 fWriteByte(fo, 0, &bxor); // header block
3895 fWriteByte(fo, 0, &bxor); // 'basic' flag
3896 if (tapeLoaderName[0]) {
3897 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3898 } else {
3899 fWriteByte(fo, 'c', &bxor);
3900 fWriteByte(fo, 'a', &bxor);
3901 fWriteByte(fo, 'r', &bxor);
3902 fWriteByte(fo, 'g', &bxor);
3903 fWriteByte(fo, 'a', &bxor);
3904 fWriteByte(fo, 'd', &bxor);
3905 fWriteByte(fo, 'o', &bxor);
3906 fWriteByte(fo, 'r', &bxor);
3907 fWriteByte(fo, ' ', &bxor);
3908 fWriteByte(fo, ' ', &bxor);
3910 fWriteWord(fo, pos, &bxor); // length
3911 fWriteWord(fo, 10, &bxor); // start
3912 fWriteWord(fo, pos, &bxor); // length2
3913 fWriteByte(fo, bxor, NULL);
3914 // write data
3915 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
3916 bxor = 0;
3917 fWriteByte(fo, 0xFFU, &bxor); // data block
3918 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
3919 fWriteByte(fo, bxor, NULL);
3923 static void saveTap (const char *basename) {
3924 char *fname;// = malloc(strlen(basename)+16);
3925 char blkname[128];
3926 int start = 0, len, f;
3927 uint8_t bxor;
3928 FILE *fo;
3930 //fname = strprintf("%s/%s.tap", optOutputDir, basename);
3931 fname = buildOutputFileName(basename, "tap");
3932 fo = fopen(fname, "wb");
3933 free(fname);
3934 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
3935 printf("out: %s.tap\n", basename);
3936 if (optRunTape) saveTapCargador(fo);
3937 while (findChunkFrom(start, &start, &len)) {
3938 // write header
3939 if (tapeLoaderName[0]) {
3940 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3941 memcpy(blkname, tapeLoaderName, 10);
3942 } else {
3943 sprintf(blkname, "c%04X:%04X", start, len);
3945 //printf(" block: %s\n", blkname);
3946 fWriteWord(fo, 19, NULL); // length of header
3947 bxor = 0;
3948 fWriteByte(fo, 0, &bxor); // header block
3949 fWriteByte(fo, 3, &bxor); // 'code' flag
3950 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
3951 fWriteWord(fo, len, &bxor);
3952 fWriteWord(fo, start, &bxor);
3953 fWriteWord(fo, 32768, &bxor);
3954 fWriteByte(fo, bxor, NULL);
3955 // write data
3956 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
3957 bxor = 0;
3958 fWriteByte(fo, 0xFFU, &bxor); // data block
3959 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
3960 fWriteByte(fo, bxor, NULL);
3961 start += len;
3964 for (int dfpass = 0; dfpass < 2; ++dfpass) {
3965 // write data files
3966 for (OutDataFile *off = datafiles; off; off = off->next) {
3967 if (off->type == 1) continue; // skip +3DOS bootsector
3968 if (off->type != 'C' || off->size > 65533) {
3969 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3970 continue;
3972 if (dfpass == 0) {
3973 if (!off->hasstart) continue;
3974 } else {
3975 if (off->hasstart) continue;
3977 snprintf(blkname, 10, "%s", off->name);
3978 blkname[10] = 0; // just in case
3979 while (strlen(blkname) < 10) strcat(blkname, " ");
3980 // write data block
3981 fWriteWord(fo, 19, NULL); // length of header
3982 bxor = 0;
3983 fWriteByte(fo, 0, &bxor); // header block
3984 fWriteByte(fo, 3, &bxor); // 'code' flag
3985 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
3986 fWriteWord(fo, off->size, &bxor);
3987 fWriteWord(fo, off->start, &bxor);
3988 fWriteWord(fo, 32768, &bxor);
3989 fWriteByte(fo, bxor, NULL);
3990 // write data
3991 fWriteWord(fo, off->size+2, NULL); // plus type and checkbyte
3992 bxor = 0;
3993 fWriteByte(fo, 0xFFU, &bxor); // data block
3994 for (f = 0; f < off->size; ++f) fWriteByte(fo, off->data[f], &bxor);
3995 fWriteByte(fo, bxor, NULL);
3999 fclose(fo);
4003 ///////////////////////////////////////////////////////////////////////////////
4004 // pseudoinstructions
4006 // note that processCurrentLine() will NOT skip to the next line before
4007 // calling pseudoinstruction handler!
4008 // note that processCurrentLine() will skip current line after calling
4009 // pseudoinstruction handler!
4011 static int wasOrg = 0;
4012 static int wasClr = 0;
4015 // ////////////////////////////////////////////////////////////////////////// //
4016 // print message using printf-like syntax
4017 // doesn't print new line
4018 static void processPrintf (FILE *fo, const char *fmt) {
4019 if (!fmt || !fmt[0]) return;
4020 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
4021 char *tempstr = NULL;
4022 size_t tempsize = 0;
4023 char *fmtcopy = malloc(strlen(fmt)+1);
4024 strcpy(fmtcopy, fmt);
4025 char *currfmt = fmtcopy;
4026 char tempbuf[512];
4027 int stlen;
4028 char *strarg;
4029 int defined;
4030 int32_t exprval;
4031 while (*currfmt) {
4032 /* find '%' */
4033 char *prcs = strchr(currfmt, '%');
4034 if (!prcs || !prcs[1]) {
4035 /* no more formatting; print the tail and exit the loop */
4036 fprintf(fo, "%s", currfmt);
4037 break;
4039 /* is this "%%"? */
4040 int docheck = 1;
4041 if (prcs[1] == '%') {
4042 ++prcs;
4043 docheck = 0;
4045 /* print up to `prcs` */
4046 if (prcs > currfmt) {
4047 size_t partlen = (ptrdiff_t)(prcs-currfmt);
4048 if (partlen+1 > tempsize) {
4049 tempsize = ((partlen+8)|0xff)+1;
4050 tempstr = realloc(tempstr, tempsize);
4052 memcpy(tempstr, currfmt, partlen);
4053 tempstr[partlen] = 0;
4054 fprintf(fo, "%s", tempstr);
4056 currfmt = ++prcs; /* skip percent */
4057 if (!docheck) continue;
4058 /* parse format */
4059 char sign = ' ';
4060 int width = 0;
4061 int zerofill = 0;
4062 char ftype = ' ';
4063 if (*currfmt == '+' || *currfmt == '-') sign = *currfmt++;
4064 if (sign != '-' && *currfmt == '0') zerofill = 1;
4065 while (isDigit(*currfmt)) { width = width*10+((*currfmt)-'0'); ++currfmt; }
4066 if (width > 256) width = 256;
4067 ftype = *currfmt++;
4068 if (!ftype) break; /* oops */
4069 if (!eatComma()) fatal("out of arguments for string format");
4070 switch (ftype) {
4071 case 's': /* string */
4072 stlen = 0;
4073 switch (strSkipSpaces(currLine)[0]) {
4074 case '"': case '\'': /* string literal? */
4075 strarg = getStrArg(&stlen);
4076 break;
4077 default: /* expression */
4078 strarg = getStrExprArgFmt();
4079 stlen = (int)strlen(strarg);
4080 break;
4082 /* left pad */
4083 if (sign != '-' && stlen < width) {
4084 int padlen = width-stlen;
4085 memset(tempbuf, ' ', padlen);
4086 tempbuf[padlen+1] = 0;
4087 fprintf(fo, "%s", tempbuf);
4089 fprintf(fo, "%s", strarg);
4090 /* right pad */
4091 if (sign == '-' && stlen < width) {
4092 int padlen = width-stlen;
4093 memset(tempbuf, ' ', padlen);
4094 tempbuf[padlen+1] = 0;
4095 fprintf(fo, "%s", tempbuf);
4097 break;
4098 case 'd': /* decimal */
4099 defined = 1;
4100 exprval = getExprArg(&defined, NULL);
4101 if (width && zerofill) {
4102 fprintf(fo, "%0*d", width, exprval);
4103 } else if (width) {
4104 fprintf(fo, "%*d", (sign != '-' ? width : -width), exprval);
4105 } else {
4106 fprintf(fo, "%d", exprval);
4108 break;
4109 case 'c': /* char */
4110 defined = 1;
4111 exprval = getExprArg(&defined, NULL);
4112 if (exprval <= 0 || exprval == '?' || exprval > 255) exprval = '?';
4113 if (width) {
4114 fprintf(fo, "%*c", (sign != '-' ? width : -width), exprval);
4115 } else {
4116 fprintf(fo, "%c", exprval);
4118 break;
4119 case 'u': /* unsigned */
4120 defined = 1;
4121 exprval = getExprArg(&defined, NULL);
4122 if (width && zerofill) {
4123 fprintf(fo, "%0*u", width, (unsigned)(exprval&0xffff));
4124 } else if (width) {
4125 fprintf(fo, "%*u", (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
4126 } else {
4127 fprintf(fo, "%u", (unsigned)(exprval&0xffff));
4129 break;
4130 case 'x': case 'X': /* hex */
4131 defined = 1;
4132 exprval = getExprArg(&defined, NULL);
4133 if (width && zerofill) {
4134 fprintf(fo, (ftype == 'x' ? "%0*x" : "%0*X"), width, (unsigned)(exprval&0xffff));
4135 } else if (width) {
4136 fprintf(fo, (ftype == 'x' ? "%*x" : "%*X"), (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
4137 } else {
4138 fprintf(fo, (ftype == 'x' ? "%x" : "%X"), (unsigned)(exprval&0xffff));
4140 break;
4141 default:
4142 if (ftype <= 0 || ftype == 127) ftype = '?';
4143 fatal("invalid format specifier: '%c'", ftype);
4144 break;
4147 if (tempstr) free(tempstr);
4148 free(fmtcopy);
4152 ///////////////////////////////////////////////////////////////////////////////
4153 // user error
4155 static int piERROR (void) {
4156 int len = 0;
4157 char *res = getStrArg(&len);
4158 fprintf(stdout, "*** USER ERROR: ");
4159 processPrintf(stdout, res);
4160 fputc('\n', stdout);
4161 fatal("user error abort");
4162 return PI_SKIP_LINE;
4166 static int piWARNING (void) {
4167 int len = 0;
4168 char *res = getStrArg(&len);
4169 fprintf(stdout, "*** USER WARNING ");
4170 if (currSrcLine) {
4171 fprintf(stdout, "at file %s, line %d: ", currSrcLine->fname, currSrcLine->lineNo);
4172 } else {
4173 fprintf(stdout, "somewhere in time: ");
4175 processPrintf(stdout, res);
4176 fputc('\n', stdout);
4177 return PI_SKIP_LINE;
4181 ///////////////////////////////////////////////////////////////////////////////
4182 // user warnings
4184 static int piPrintfCommon (int passNo) {
4185 if (passNo < 0 || pass == passNo) {
4186 int len = 0;
4187 char *res = getStrArg(&len);
4188 processPrintf(stdout, res);
4189 fputc('\n', stdout);
4191 return PI_SKIP_LINE;
4195 static int piDISPLAYX (int passNo, int asHex) {
4196 for (;;) {
4197 if (isStrArg()) {
4198 int len = 0;
4199 char *res = getStrArg(&len);
4201 if (passNo < 0 || pass == passNo) printf("%s", res);
4202 } else {
4203 int defined = 1;
4204 int32_t v = getExprArg(&defined, NULL);
4206 if (passNo < 0 || pass == passNo) {
4207 if (asHex) printf("%04X", (unsigned int)v);
4208 else printf("%d", v);
4211 if (!eatComma()) break;
4213 return PI_SKIP_LINE;
4217 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
4218 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
4219 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
4220 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
4221 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
4222 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
4224 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
4225 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
4226 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
4229 ///////////////////////////////////////////////////////////////////////////////
4230 // ORG, DISP, etc.
4232 static int piORG (void) {
4233 int defined = 1;
4234 int32_t res = getOneExprArg(&defined, NULL);
4236 if (!defined) fatal("sorry, ORG operand value must be known here");
4237 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
4238 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
4239 pc = disp = res;
4240 if (!wasOrg) {
4241 wasOrg = 1;
4242 ent = res;
4243 if (!wasClr && res > 0) clrAddr = res-1;
4245 return PI_CONT_LINE;
4249 static int piDISP (void) {
4250 int defined = 1;
4251 int32_t res = getOneExprArg(&defined, NULL);
4253 if (!defined) fatal("sorry, DISP operand value must be known here");
4254 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
4255 //printf("DISP=%d\n", res);
4256 disp = res;
4257 return PI_CONT_LINE;
4261 static int piENDDISP (void) {
4262 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
4263 checkExprEnd();
4264 disp = pc;
4265 return PI_CONT_LINE;
4269 static int piENT (void) {
4270 int defined = 1;
4271 int32_t res = getOneExprArg(&defined, NULL);
4273 //if (!defined) fatal("sorry, ENT operand value must be known here");
4274 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
4275 ent = res;
4276 return PI_CONT_LINE;
4280 static int piCLR (void) {
4281 int defined = 1;
4282 int32_t res = getOneExprArg(&defined, NULL);
4284 //if (!defined) fatal("sorry, CLR operand value must be known here");
4285 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
4286 clrAddr = res;
4287 wasClr = 1;
4288 return PI_CONT_LINE;
4292 // RESERVE start, count
4293 static int piRESERVE (void) {
4294 int defined = 1, start;
4295 int32_t res = getExprArg(&defined, NULL);
4296 if (!defined) fatal("sorry, RESERVE operand values must be known here");
4297 if (res < 0 || res > 65535) fatal("invalid RESERVE address value: %d", res);
4298 start = res;
4299 if (!eatComma()) fatal("RESERVE needs 2 args");
4300 res = getOneExprArg(&defined, NULL);
4301 if (!defined) fatal("sorry, RESERVE operand values must be known here");
4302 if (res < 0 || res > 65535) fatal("invalid RESERVE length value: %d", res);
4303 while (res--) {
4304 if (memused[start]) fatal("trying to reserve already used memory at #%04X", start);
4305 memresv[start++] = 1;
4307 return PI_CONT_LINE;
4311 static int piALIGN (void) {
4312 int defined = 1;
4313 int32_t res;
4315 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
4316 res = getOneExprArg(&defined, NULL);
4317 if (!defined) fatal("sorry, ALIGN operand value must be known here");
4318 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
4319 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
4320 if (res > 0 && pc%res != 0) {
4321 pc /= res;
4322 pc *= res;
4323 pc += res;
4324 disp = pc;
4326 return PI_CONT_LINE;
4330 static int piDISPALIGN (void) {
4331 int defined = 1;
4332 int32_t res = getOneExprArg(&defined, NULL);
4334 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
4335 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
4336 if (res > 0 && disp%res != 0) {
4337 disp /= res;
4338 disp *= res;
4339 disp += res;
4341 return PI_CONT_LINE;
4345 ///////////////////////////////////////////////////////////////////////////////
4346 // DEFx
4348 // DEFINCR operations
4349 enum {
4350 DFI_SHR,
4351 DFI_SHL,
4352 DFI_SAR, // arith shift, extend bit 7
4353 DFI_SAL, // arith shift, extend bit 0
4354 DFI_ROR,
4355 DFI_ROL,
4356 DFI_AND,
4357 DFI_XOR,
4358 DFI_OR,
4359 DFI_ADD,
4360 DFI_SUB,
4361 DFI_MUL,
4362 DFI_DIV,
4363 DFI_MOD,
4366 typedef struct DefIncrOp_s DefIncrOp;
4367 struct DefIncrOp_s {
4368 DefIncrOp *next;
4369 uint16_t operator; // DFI_xxx
4370 uint16_t operand;
4373 static DefIncrOp *dfi_list = NULL;
4374 //static int defIncr = 0;
4377 static void dfi_free (void) {
4378 while (dfi_list) {
4379 DefIncrOp *c = dfi_list;
4380 dfi_list = c->next;
4381 free(c);
4386 //FIXME: make this faster
4387 #define DFI_XSHIFT(sop_) do { \
4388 for (uint16_t f = 0; f < opnd; ++f) { \
4389 val = ((val&xmask) sop_ 1)|(val&andmask ? ormask : 0); \
4391 } while (0)
4394 static int32_t dfi_apply (int32_t val, int isbyte) {
4395 const uint16_t xmask = (isbyte ? 0xff : 0xffff);
4396 val &= xmask;
4397 uint16_t andmask, ormask;
4398 for (DefIncrOp *dop = dfi_list; dop; dop = dop->next) {
4399 uint16_t opnd = dop->operand;
4400 switch (dop->operator) {
4401 case DFI_SHR:
4402 if (!opnd) break;
4403 if (opnd > (isbyte ? 7 : 15)) { val = 0; break; }
4404 val = (val&xmask)>>opnd;
4405 break;
4406 case DFI_SHL:
4407 if (!opnd) break;
4408 if (opnd > (isbyte ? 7 : 15)) { val = 0; break; }
4409 val = (val&xmask)<<opnd;
4410 break;
4411 case DFI_SAR:
4412 if (!opnd) break;
4413 if (isbyte) {
4414 if (opnd > 7) { val = (val&0x80 ? 0xff : 0x00); break; }
4415 andmask = ormask = 0x80;
4416 } else {
4417 if (opnd > 15) { val = (val&0x8000 ? 0xffff : 0x0000); break; }
4418 andmask = ormask = 0x8000;
4420 DFI_XSHIFT(>>);
4421 break;
4422 case DFI_SAL:
4423 if (!opnd) break;
4424 if (opnd > (isbyte ? 7 : 15)) { val = (val&0x01 ? 0xff : 0x00); break; }
4425 andmask = ormask = 0x01;
4426 DFI_XSHIFT(<<);
4427 break;
4428 case DFI_ROR:
4429 opnd &= (isbyte ? 7 : 15);
4430 if (!opnd) break;
4431 andmask = 0x01;
4432 ormask = (isbyte ? 0x80 : 0x8000);
4433 DFI_XSHIFT(>>);
4434 break;
4435 case DFI_ROL:
4436 opnd &= (isbyte ? 7 : 15);
4437 if (!opnd) break;
4438 andmask = (isbyte ? 0x80 : 0x8000);
4439 ormask = 0x01;
4440 DFI_XSHIFT(<<);
4441 break;
4442 case DFI_AND:
4443 val &= opnd;
4444 break;
4445 case DFI_XOR:
4446 val ^= opnd;
4447 break;
4448 case DFI_OR:
4449 val |= opnd;
4450 break;
4451 case DFI_ADD:
4452 val += opnd;
4453 break;
4454 case DFI_SUB:
4455 val -= opnd;
4456 break;
4457 case DFI_MUL:
4458 val *= opnd;
4459 break;
4460 case DFI_DIV:
4461 if (opnd) val /= opnd; else val = 0;
4462 break;
4463 case DFI_MOD:
4464 if (opnd) val %= opnd; else val = 0;
4465 break;
4467 val &= xmask;
4469 return val;
4473 static int piDEFINCR (void) {
4474 #if 0
4475 int defined = 1;
4476 int32_t cnt = getOneExprArg(&defined, NULL);
4477 if (!defined) fatal("DEFINCR: increment must be defined");
4478 defIncr = cnt;
4479 return PI_CONT_LINE;
4480 #else
4481 dfi_free();
4482 if (isStrArg()) {
4483 int len = 0;
4484 char *rstr = getStrArg(&len);
4485 if (!rstr || !strEquCI(rstr, "expr")) fatal("invalid DEFINCR command (\"expr\" expected)");
4486 DefIncrOp *last = NULL;
4487 while (eatComma()) {
4488 len = 0;
4489 rstr = getStrArg(&len);
4490 if (!rstr || len != 3) fatal("invalid DEFINCR operator");
4491 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4492 if (strEquCI(rstr, "SHR")) dop->operator = DFI_SHR;
4493 else if (strEquCI(rstr, "SHL")) dop->operator = DFI_SHL;
4494 else if (strEquCI(rstr, "SAR")) dop->operator = DFI_SAR;
4495 else if (strEquCI(rstr, "SAL")) dop->operator = DFI_SAL;
4496 else if (strEquCI(rstr, "ROR")) dop->operator = DFI_ROR;
4497 else if (strEquCI(rstr, "ROL")) dop->operator = DFI_ROL;
4498 else if (strEquCI(rstr, "AND")) dop->operator = DFI_AND;
4499 else if (strEquCI(rstr, "XOR")) dop->operator = DFI_XOR;
4500 else if (strEquCI(rstr, "OR")) dop->operator = DFI_OR;
4501 else if (strEquCI(rstr, "ADD")) dop->operator = DFI_ADD;
4502 else if (strEquCI(rstr, "SUB")) dop->operator = DFI_SUB;
4503 else if (strEquCI(rstr, "MUL")) dop->operator = DFI_MUL;
4504 else if (strEquCI(rstr, "DIV")) dop->operator = DFI_DIV;
4505 else if (strEquCI(rstr, "MOD")) dop->operator = DFI_MOD;
4506 else fatal("invalid DEFINCR operator '%s'", rstr);
4507 dop->next = NULL;
4508 if (!eatComma()) fatal("DEFINCR: operand expected");
4509 int defined = 1;
4510 int32_t res = getExprArg(&defined, NULL);
4511 if (!defined) fatal("DEFINCR: operand must be defined");
4512 if (res < 0) fatal("DEFINCR: operand must be positive");
4513 res &= 0xffff;
4514 dop->operand = (uint16_t)res;
4515 if (last) last->next = dop; else dfi_list = dop;
4516 last = dop;
4518 } else {
4519 int defined = 1;
4520 int32_t cnt = getOneExprArg(&defined, NULL);
4521 if (!defined) fatal("DEFINCR: increment must be defined");
4522 //defIncr = cnt;
4523 if (cnt > 0) {
4524 cnt &= 0xffff;
4525 if (cnt) {
4526 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4527 dop->next = NULL;
4528 dop->operator = DFI_ADD;
4529 dop->operand = cnt;
4530 dfi_list = dop;
4532 } else {
4533 cnt &= 0xffff; //UB, i don't care
4534 if (cnt) {
4535 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4536 dop->next = NULL;
4537 dop->operator = DFI_SUB;
4538 dop->operand = cnt;
4539 dfi_list = dop;
4543 #endif
4544 return PI_CONT_LINE;
4548 static int piDEFBW (int isWord) {
4549 int32_t res;
4550 for (;;) {
4551 int defined = 0, fixuptype = UR_FIXUP_NONE;
4553 if (isStrArg()) {
4554 int f, len = 0;
4555 char *rstr = getStrExprArg(&len, isWord);
4556 if (rstr) {
4557 for (f = 0; f < len; ++f) {
4558 int32_t b = (uint8_t)rstr[f];
4559 //b += defIncr;
4560 b = dfi_apply(b, 1/*isbyte*/);
4561 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
4562 if (b < 0) b += 256;
4563 emitByte(b);
4565 if (!eatComma()) break;
4566 continue;
4567 } else {
4568 defined = 1;
4569 res = len;
4571 } else {
4572 res = getExprArg(&defined, &fixuptype);
4576 //int32_t res = getExprArg(&defined, &fixuptype);
4578 if (pass > 0 && !defined) fatal("undefined operand");
4579 //res += defIncr;
4580 res = dfi_apply(res, !isWord/*isbyte*/);
4581 if (isWord) {
4582 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
4583 if (res < 0) res += 65536;
4584 if (isWord == 1) {
4585 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 2);
4586 emitWord(res);
4587 } else {
4588 /* reversed word */
4589 switch (fixuptype) {
4590 case UR_FIXUP_WORD: /* swapped bytes */
4591 urasm_fixup_operand(NULL, pc, disp, UR_FIXUP_HIBYTE, 1);
4592 urasm_fixup_operand(NULL, pc, disp+1, UR_FIXUP_LOBYTE, 1);
4593 break;
4594 case UR_FIXUP_LOBYTE:
4595 case UR_FIXUP_HIBYTE:
4596 warningMsg("non-word fixup for reversed word; wtf?!");
4597 break;
4598 default: break;
4600 emitRWord(res);
4602 } else {
4603 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
4604 if (fixuptype != UR_FIXUP_NONE) {
4605 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
4606 urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
4608 if (res < 0) res += 256;
4609 emitByte(res);
4612 if (!eatComma()) break;
4614 return PI_CONT_LINE;
4617 static int piDEFB (void) { return piDEFBW(0); }
4618 static int piDEFW (void) { return piDEFBW(1); }
4619 static int piDEFR (void) { return piDEFBW(2); }
4622 static int piDEFS (void) {
4623 for (;;) {
4624 int32_t bt, f;
4625 int defined = 0, fixuptype = UR_FIXUP_NONE;
4626 int32_t res = getExprArg(&defined, &fixuptype);
4628 if (pass > 0 && !defined) fatal("undefined operand");
4629 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
4630 if (*currLine && currLine[0] == ',') {
4631 (void)eatComma();
4632 bt = getExprArg(&defined, NULL);
4633 if (pass > 0 && !defined) fatal("undefined operand");
4634 //bt += defIncr;
4635 bt = dfi_apply(bt, 1/*isbyte*/);
4636 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
4637 if (bt < 0) bt += 256;
4638 if (fixuptype != UR_FIXUP_NONE) {
4639 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
4641 for (f = 0; f < res; ++f) {
4642 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
4643 emitByte(bt);
4645 } else {
4646 pc += res; disp += res;
4648 if (!eatComma()) break;
4650 return PI_CONT_LINE;
4654 /* bit 0: put '\0' */
4655 /* bit 1: set bit 7 of last byte */
4656 /* bit 2: put length */
4657 static int piDEFSTR (int type) {
4658 for (;;) {
4659 if (isStrArg()) {
4660 int f, len = 0;
4661 char *res = getStrArg(&len);
4663 if (type&0x04) {
4664 if (len > 255) fatal("string too long");
4665 emitByte(len);
4667 for (f = 0; f < len; ++f) {
4668 uint8_t b = (uint8_t)res[f];
4670 if ((type&0x02) && f == len-1) b |= 0x80;
4671 emitByte(b);
4673 if (type&0x01) emitByte(0);
4674 } else {
4675 int defined = 1;
4676 int32_t v = getExprArg(&defined, NULL);
4678 if (pass > 0 && !defined) fatal("undefined expression");
4679 if (!defined) v = 0; else v = dfi_apply(v, 1/*isbyte*/);
4680 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
4681 if (v < 0) v += 256;
4682 emitByte(v);
4684 if (!eatComma()) break;
4686 return PI_CONT_LINE;
4690 static int piDEFM (void) { return piDEFSTR(0x00); }
4691 static int piDEFZ (void) { return piDEFSTR(0x01); }
4692 static int piDEFX (void) { return piDEFSTR(0x02); }
4693 static int piDEFC (void) { return piDEFSTR(0x04); }
4696 ///////////////////////////////////////////////////////////////////////////////
4697 // INCBIN
4699 /* INCBIN "name"[,maxlen[,offset]] */
4700 static int piINCBIN (void) {
4701 int system = 0;
4702 char *fn, qCh;
4703 uint8_t bt;
4704 FILE *fl;
4705 int maxlen = 65536;
4706 int offset = 0;
4707 char *args = currLine;
4709 if (!currLine[0]) fatal("INCBIN without file name");
4711 if (isStrArg()) {
4712 qCh = *args++;
4713 system = 0;
4714 } else if (currLine[0] == '<') {
4715 qCh = '>'; ++args;
4716 system = 1;
4717 } else {
4718 qCh = 0;
4719 system = 0;
4722 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4723 int softinclude = 0;
4724 if (fn[0] == '?') { softinclude = 1; ++fn; while (isSpace(*fn)) ++fn; }
4725 if (!fn[0]) fatal("INCBIN: empty file name");
4726 // now fix the name
4727 char *fname = createIncludeName(fn, system, NULL, NULL);
4728 memmove(currLine, args, strlen(args)+1);
4730 // maxlen
4731 if (currLine[0] == ',') {
4732 int defined = 1;
4733 (void)eatComma();
4734 maxlen = getExprArg(&defined, NULL);
4735 if (!defined) fatal("INCBIN: undefined maxlen");
4736 if (maxlen < 1) { free(fname); return 1; } // nothing to do
4739 // offset (negative: from the end)
4740 if (currLine[0] == ',') {
4741 int defined = 1;
4742 (void)eatComma();
4743 offset = getOneExprArg(&defined, NULL);
4744 if (!defined) fatal("INCBIN: undefined offset");
4745 } else {
4746 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
4749 fl = fopen(fname, "rb");
4750 if (!fl) {
4751 if (!softinclude) fatal("INCBIN: file not found: '%s'", fname);
4752 } else {
4753 if (offset) {
4754 if (fseek(fl, offset, (offset < 0 ? SEEK_END : SEEK_SET)) < 0) {
4755 fclose(fl);
4756 fatal("INCBIN: error seeking to %d in file '%s'", offset, fname);
4759 while (maxlen-- > 0) {
4760 int res = fread(&bt, 1, 1, fl);
4761 if (!res) break;
4762 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: '%s'", fname); }
4763 emitByte(bt);
4765 fclose(fl);
4768 free(fname);
4769 return PI_SKIP_LINE;
4773 /* DATABIN "name"[,len[,offset[,codeaddr]]] */
4774 /* DATABIN "name|dfname"[,len[,offset[,codeaddr]]] */
4775 static int piDATABINcommon (int ascode) {
4776 if (pass != 1) return PI_SKIP_LINE;
4778 int system = 0;
4779 char *fn, qCh;
4780 int maxlen = -1;
4781 int offset = 0;
4782 int codeaddr = 32768;
4783 int hasstart = 0;
4784 char *args = currLine;
4786 if (!currLine[0]) fatal("DATABIN without file name");
4788 if (isStrArg()) {
4789 qCh = *args++;
4790 system = 0;
4791 } else if (currLine[0] == '<') {
4792 qCh = '>'; ++args;
4793 system = 1;
4794 } else {
4795 qCh = 0;
4796 system = 0;
4799 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4800 int allowskip = 0;
4801 if (fn[0] == '?') { allowskip = 1; ++fn; while (isSpace(*fn)) ++fn; }
4802 if (!fn[0]) fatal("DATABIN: empty file name");
4803 char *fnameup = strdup(fn);
4804 memmove(currLine, args, strlen(args)+1);
4806 // maxlen
4807 if (currLine[0] == ',') {
4808 int defined = 1;
4809 (void)eatComma();
4810 maxlen = getExprArg(&defined, NULL);
4811 if (!defined) fatal("DATABIN: undefined length");
4812 if (maxlen < 1) { free(fnameup); return 1; } // nothing to do
4815 // offset (negative: from the end)
4816 if (currLine[0] == ',') {
4817 int defined = 1;
4818 (void)eatComma();
4819 offset = getExprArg(&defined, NULL);
4820 if (!defined) fatal("DATABIN: undefined offset");
4823 // code address
4824 if (currLine[0] == ',') {
4825 int defined = 1;
4826 (void)eatComma();
4827 codeaddr = getOneExprArg(&defined, NULL);
4828 if (!defined) fatal("DATABIN: undefined codeaddr");
4829 hasstart = 1;
4830 } else {
4831 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
4834 // find and extract "disk name"
4835 char *diskname;
4837 char *pipeptr = strchr(fnameup, '|');
4838 if (pipeptr) {
4839 if (!pipeptr[1]) fatal("empty data file output name");
4840 diskname = strdup(pipeptr+1);
4841 *pipeptr = 0;
4842 } else {
4843 // build output name from disk name
4844 char *slp = strrchr(fnameup, '/');
4845 #ifdef WIN32
4846 char *slp1 = strrchr(fnameup, '\\');
4847 if (slp1 && (!slp || slp1 > slp)) slp = slp1;
4848 #endif
4849 if (slp) {
4850 ++slp;
4851 if (!slp[0]) fatal("empty data file output name");
4852 diskname = strdup(slp);
4853 } else {
4854 diskname = strdup(fnameup);
4858 // now fix the name
4859 char *fname = createIncludeName(fnameup, system, NULL, NULL);
4860 free(fnameup);
4862 OutDataFile *off = appendDataFile(fname, offset, maxlen, allowskip);
4864 if (off) {
4865 off->type = 'C';
4866 off->name = diskname;
4867 off->start = codeaddr&0xffffU;
4868 off->hasstart = (hasstart || ascode);
4869 //for (char *s = off->name; *s; ++s) *s = toUpper(*s);
4870 printf("data file: %s (start=%u; size=%u; std=%d)\n", off->name, off->start, off->size, off->hasstart);
4873 free(fname);
4874 return PI_SKIP_LINE;
4878 static int piDATABIN (void) { return piDATABINcommon(0); }
4879 static int piCODEBIN (void) { return piDATABINcommon(1); }
4882 ///////////////////////////////////////////////////////////////////////////////
4883 // INCLUDE
4885 /* INCLUDE "name" */
4886 static int piINCLUDE (void) {
4887 int system = 0;
4888 char *fn, qCh;
4889 char *args = currLine;
4890 if (!currLine[0]) fatal("INCLUDE without file name");
4891 if (isStrArg()) {
4892 qCh = *args++;
4893 system = 0;
4894 } else if (currLine[0] == '<') {
4895 qCh = '>'; ++args;
4896 system = 1;
4897 } else {
4898 qCh = 0;
4899 system = 0;
4901 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4902 // flags
4903 int softinclude = 0;
4904 if (fn[0] == '?') { softinclude = 1; ++fn; while (isSpace(*fn)) ++fn; }
4905 if (!fn[0]) fatal("INCLUDE: empty file name");
4906 // load
4907 if (asmTextInclude(fn, system, softinclude) != 0) {
4908 if (!softinclude) fatal("INCLUDE: some shit happens!");
4910 return PI_SKIP_LINE;
4914 ///////////////////////////////////////////////////////////////////////////////
4915 // MODULE, ENDMODULE
4917 static int piENDMODULE (void) {
4918 if (!currModule) fatal("ENDMODULE without MODULE");
4919 const char *mn = NULL;
4920 if (currLine[0]) mn = getOneLabelArg();
4921 moduleClose(mn);
4922 return PI_SKIP_LINE;
4926 static int piMODULE (void) {
4927 ModuleInfo *mi;
4928 char *mn;
4929 SourceLine *ol = currSrcLine;
4930 int inum;
4931 if (currModule) fatal("no nested modules allowed");
4932 mn = getOneLabelArg();
4933 if (!urasm_is_valid_name(mn)) fatal("invalid module name: %s", mn);
4934 mi = moduleFind(mn);
4935 //fprintf(stderr, "+++MODULE: [%s] %p (seen=%d)\n", mn, mi, (mi ? mi->seen : -1));
4936 if (mi) {
4937 if (mi->seen) {
4938 if (strcmp(mi->fname, currSrcLine->fname)) {
4939 fatal("duplicate module definition; previous was in %s", mi->fname);
4941 /* skip module */
4942 nextSrcLine(); /* skip "MODULE" line */
4943 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
4944 setCurSrcLine(ol);
4945 fatal("no ENDMODULE");
4947 if (inum == 0) fatal("no nested modules allowed");
4948 currModule = mi;
4949 moduleOpen();
4950 //skipInstruction(); //k8:wtf?!
4951 return piENDMODULE();
4953 } else {
4954 mi = moduleAdd(mn, currSrcLine->fname);
4956 mi->seen = 1;
4957 currModule = mi;
4958 moduleOpen();
4959 return PI_SKIP_LINE;
4963 /* Z80, Z80N */
4964 static int piMODEL (void) {
4965 char *mn = getOneIdArgLo();
4966 if (strSkipSpaces(currLine)[0]) fatal("only one model name expected");
4967 if (strcmp(mn, "z80") == 0) urasm_allow_zxnext = 0;
4968 else if (strcmp(mn, "z80a") == 0) urasm_allow_zxnext = 0;
4969 else if (strcmp(mn, "z80n") == 0) urasm_allow_zxnext = 1;
4970 else if (strcmp(mn, "z80next") == 0) urasm_allow_zxnext = 1;
4971 else if (strcmp(mn, "zxnext") == 0) urasm_allow_zxnext = 1;
4972 else fatal("invalid model name: %s", mn);
4973 return PI_SKIP_LINE;
4977 ////////////////////////////////////////////////////////////////////////////////
4978 // $SAVECODE "filename",addr,size [,ldaddr [,options...]]
4980 // options:
4981 // wipe -- wipe saved area (mark it as unused)
4983 static int piSAVECODE (void) {
4984 //if (pass != 1) return PI_SKIP_LINE;
4985 //char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4986 //matchDelim(',');
4988 int optWipe = 0, optData = 0;
4989 char *fn, qCh;
4990 char *args = currLine;
4992 if (!currLine[0]) fatal("SAVECODE without file name");
4994 if (isStrArg()) {
4995 qCh = *args++;
4996 } else if (currLine[0] == '<') {
4997 qCh = '>'; ++args;
4998 } else {
4999 qCh = 0;
5002 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
5003 if (!fn[0]) fatal("DATABIN: empty file name");
5005 char *fname = strdup(fn);
5006 memmove(currLine, args, strlen(args)+1);
5008 // addr
5009 matchDelim(',');
5010 int defined = 1;
5011 int staddr = getExprArg(&defined, NULL);
5012 if (!defined) fatal("SAVECODE: undefined start address");
5013 if (staddr < 0 || staddr > 65535) fatal("SAVECODE: invalid start address (%d)", staddr);
5015 // size
5016 matchDelim(',');
5017 defined = 1;
5018 int size = getExprArg(&defined, NULL);
5019 if (!defined) fatal("SAVECODE: undefined size");
5020 if (size < 0 || size > 65535 || staddr+size > 65536) fatal("SAVECODE: invalid size");
5022 // load address
5023 int ldaddr = staddr;
5024 if (checkDelim(',')) {
5025 defined = 1;
5026 ldaddr = getExprArg(&defined, NULL);
5027 if (!defined) fatal("SAVECODE: undefined load address");
5028 if (ldaddr < 0 || ldaddr > 65535) fatal("SAVECODE: invalid load address");
5031 // parse options
5032 while (checkDelim(',')) {
5033 char *nstr = getLabelArg(0/*checkdelim*/);
5034 if (strEquCI(nstr, "wipe")) { optWipe = 1; continue; }
5035 if (strEquCI(nstr, "data")) { optData = 1; continue; }
5036 fatal("SAVECODE: unknown option '%s'", nstr);
5038 if (!isLineEnd()) fatal("SAVECODE: unknown extra args");
5040 // save, if we are on the second pass
5041 if (pass == 1 && size > 0) {
5042 OutDataFile *off = appendDataData(memory+(unsigned)staddr, (unsigned)staddr, (unsigned)size);
5043 if (!off) fatal("SAVECODE: out of memory");
5044 off->name = fname;
5045 off->hasstart = !optData;
5046 fname = NULL;
5049 if (optWipe) memset(memused+staddr, 0, size);
5051 if (fname) free(fname);
5052 return PI_SKIP_LINE;
5056 ////////////////////////////////////////////////////////////////////////////////
5057 // STRUCT
5059 static int piENDS (void) {
5060 fatal("ENDS without STRUCT");
5061 checkOperatorEnd();
5062 return PI_SKIP_LINE;
5066 // this will be registered for each new struct
5067 // use `urCurrentOp` to check what structure we are creating
5068 // `currSeenLabel` contains current seen label
5069 static int piSTRUCT_Internal (void) {
5070 StructInfo *sth = urCurrentOp->udata;
5072 // reserve room, set default values, add labels
5073 uint16_t origPC = pc;
5074 uint16_t origDisp = disp;
5076 pc += sth->size;
5077 disp += sth->size;
5079 if (sth->zerofill) {
5080 for (uint16_t f = 0; f < sth->size; ++f) {
5081 putByte(origPC+f, 0);
5085 for (StructField *fld = sth->fields; fld; fld = fld->next) {
5086 if (fld->hasInit) {
5087 if (fld->size == 1) {
5088 const uint8_t v = (fld->initValue < 0 ? 0x100+fld->initValue : fld->initValue)&0xff;
5089 putByte(origPC+fld->ofs, v);
5090 } else if (fld->size == 2) {
5091 const uint16_t v = (fld->initValue < 0 ? 0x10000+fld->initValue : fld->initValue)&0xffff;
5092 putWord(origPC+fld->ofs, v);
5093 } else if (fld->size == 4) {
5094 uint32_t v = 0;
5095 memcpy(&v, &fld->initValue, 4);
5096 putWord(origPC+fld->ofs, v&0xffff);
5097 putWord(origPC+fld->ofs+2, (v>>16)&0xffff);
5098 } else {
5099 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth->name);
5103 if (pass) continue;
5105 //fprintf(stderr, "LBL: <%s>\n", currSeenLabel);
5106 // need to create a new label?
5107 if (!currSeenLabel[0] || !fld->name || !fld->name[0]) continue;
5109 char *nn = mangleLabelName(currSeenLabel);
5110 char *lbn = malloc(strlen(nn)+strlen(fld->name)+64);
5111 sprintf(lbn, "%s.%s", nn, fld->name);
5113 UrLabelInfo *c = urFindLabel(lbn);
5114 if (!c) c = urAddLabel(lbn);
5115 free(lbn);
5117 c->type = LBL_TYPE_STOFS;
5118 c->value = origDisp+fld->ofs;
5119 c->known = 1;
5122 // create sizeof?
5123 if (currSeenLabel[0]) {
5124 char *nn = mangleLabelName(currSeenLabel);
5125 char *lbn = malloc(strlen(nn)+strlen("_sizeof")+64);
5126 sprintf(lbn, "%s.%s", nn, "_sizeof");
5128 UrLabelInfo *c = urFindLabel(lbn);
5129 if (!c) c = urAddLabel(lbn);
5130 free(lbn);
5132 c->type = LBL_TYPE_STOFS;
5133 c->value = sth->size;
5134 c->known = 1;
5137 // now parse initial values, if there are any
5138 if (!isLineEnd()) {
5139 const int isCurly = checkDelim('{');
5140 SourceLine *stline = currSrcLine;
5142 int wasNewLine = 1; // don't require a comma
5143 for (;;) {
5144 if (!currSrcLine) {
5145 if (isCurly) {
5146 setCurSrcLine(stline);
5147 fatal("STRUCT: no closing curly bracket");
5149 break;
5151 if (isLineEnd()) {
5152 if (!isCurly) break;
5153 // load next line
5154 nextSrcLine();
5155 wasNewLine = 1;
5156 continue;
5159 if (isCurly && checkDelim('}')) {
5160 // we're done
5161 if (!isLineEnd()) fatal("STRUCT: closing curly bracket must be alone on the line");
5162 break;
5165 if (wasNewLine) {
5166 wasNewLine = 0;
5167 } else {
5168 matchDelim(',');
5171 char *nstr = getLabelArg(0/*checkdelim*/);
5173 // find field
5174 StructField *fld = sth->fields;
5175 for (; fld; fld = fld->next) if (fld->name && strcmp(fld->name, nstr) == 0) break;
5176 if (!fld) fatal("unknown field `%s` in struct `%s`", nstr, sth->name);
5178 // only `=` is allowed
5179 matchDelim('=');
5181 int defined = 1;
5182 int32_t ival = getExprArg(&defined, NULL);
5184 if (fld->size == 1) {
5185 if (defined && (ival < -128 || ival > 255)) {
5186 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5187 (fld->name ? fld->name : "<anonymous>"), ival);
5189 if (ival < -128) ival = -128;
5190 const uint8_t v = (ival < 0 ? 0x100+ival : ival)&0xff;
5191 putByte(origPC+fld->ofs, v);
5192 } else if (fld->size == 2) {
5193 if (defined && (ival < -32768 || ival > 65535)) {
5194 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5195 (fld->name ? fld->name : "<anonymous>"), ival);
5197 if (ival < -32768) ival = -32768;
5198 const uint16_t v = (ival < 0 ? 0x10000+ival : ival)&0xffff;
5199 putWord(origPC+fld->ofs, v);
5200 } else if (fld->size == 4) {
5201 uint32_t v = 0;
5202 memcpy(&v, &ival, 4);
5203 putWord(origPC+fld->ofs, v&0xffff);
5204 putWord(origPC+fld->ofs+2, (v>>16)&0xffff);
5205 } else {
5206 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth->name);
5211 return PI_SKIP_LINE;
5215 static int structCheckSizeDecl (const char *s) {
5216 if (strEquCI(s, "byte")) return 1;
5217 if (strEquCI(s, "word")) return 2;
5218 if (strEquCI(s, "address")) return 2;
5219 if (strEquCI(s, "dword")) return 4;
5220 if (strEquCI(s, "label")) return 0;
5221 if (strEquCI(s, "defs")) return -666;
5222 if (strEquCI(s, "ds")) return -666;
5223 return -1;
5227 // STRUCT name [,init_offset]
5228 static int piSTRUCT (void) {
5229 int zerofill = 0, extend = 0;
5230 int defined = 1, inum;
5231 SourceLine *stline;
5233 //fprintf(stderr, "PASS=%d\n", pass);
5234 if (pass) {
5235 stline = currSrcLine;
5236 nextSrcLine(); // skip ourself
5237 if (!setCurSrcLine(findNextInstructionFromList(&inum, "ENDS", NULL))) {
5238 setCurSrcLine(stline);
5239 fatal("STRUCT: no ENDS");
5241 return PI_SKIP_LINE;
5245 if (checkDelim('*')) extend = 1;
5246 else if (checkDelim('!')) extend = 2; // compatibility with the old code
5247 else if (checkDelim('+')) extend = 2;
5250 // flags: `[flag, flag...]`
5251 // currently, only `zero_fill` flag is defined
5252 if (checkDelim('[')) {
5253 while (!checkDelim(']')) {
5254 if (zerofill == 0 && checkIdentCI("zero_fill")) zerofill = 1;
5255 else fatal("invalid structure flag");
5256 if (!checkDelim(',')) {
5257 if (!checkDelim(']')) fatal("invalid structure flag list");
5258 break;
5263 // if `extend` is before a struct name, we are extending it without inheriting
5264 if (checkIdentCI("extend") || checkIdentCI("extends")) extend = 1;
5266 // struct name
5267 char stname[128], estname[128];
5268 stname[0] = 0;
5269 char *nstr = getLabelArg(0/*checkdelim*/);
5270 if (!nstr || !nstr[0]) fatal("structure name expected");
5271 if (strlen(nstr) > 127) fatal("structure name too long");
5272 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr)) {
5273 fatal("structure name `%s` is invalid", nstr);
5275 strcpy(stname, nstr);
5277 StructInfo *sth = NULL;
5278 if (extend == 0) {
5279 for (sth = structList; sth; sth = sth->next) {
5280 if (strcmp(sth->name, stname) == 0) {
5281 fatal("duplicate struct name `%s`!", stname);
5284 if (urFindOp(stname)) fatal("invalid struct name `%s`!", stname);
5287 if (extend || checkIdentCI("extend") || checkIdentCI("extends")) {
5288 if (extend == 0) {
5289 // inherit
5290 extend = 2;
5291 nstr = getLabelArg(0/*checkdelim*/);
5292 if (!nstr || !nstr[0]) fatal("parent structure name expected");
5293 if (strlen(nstr) > 127) fatal("parent structure name too long");
5294 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr)) {
5295 fatal("structure name `%s` is invalid", nstr);
5297 strcpy(estname, nstr);
5298 for (sth = structList; sth; sth = sth->next) {
5299 if (strcmp(sth->name, estname) == 0) break;
5301 if (!sth) fatal("STRUCT: cannot extend unknown structure `%s`", estname);
5302 } else {
5303 // extend
5304 for (sth = structList; sth; sth = sth->next) {
5305 if (strcmp(sth->name, stname) == 0) break;
5307 if (!sth) fatal("STRUCT: cannot extend unknown structure `%s`", stname);
5309 // inherit?
5310 if (extend == 2) {
5311 StructInfo *stnew = calloc(1, sizeof(StructInfo));
5312 stnew->name = strdup(stname);
5313 stnew->size = sth->size;
5314 stnew->zerofill = (sth->zerofill || zerofill);
5316 // copy fields
5317 StructField *xlast = NULL;
5318 for (StructField *fld = sth->fields; fld; fld = fld->next) {
5319 StructField *nf = calloc(1, sizeof(StructField));
5320 nf->name = (fld->name ? strdup(fld->name) : NULL);
5321 nf->ofs = fld->ofs;
5322 nf->size = fld->size;
5323 nf->initValue = fld->initValue;
5324 nf->hasInit = fld->hasInit;
5325 if (xlast) xlast->next = nf; else stnew->fields = nf;
5326 xlast = nf;
5329 sth = stnew;
5331 } else {
5332 // allocate struct header
5333 sth = calloc(1, sizeof(StructInfo));
5334 sth->name = strdup(stname);
5335 sth->zerofill = zerofill;
5338 int32_t currofs = (sth ? sth->size : 0);
5339 // parse initial offset
5340 if (checkDelim(',')) {
5341 defined = 1;
5342 currofs = getExprArg(&defined, NULL);
5343 if (!defined) fatal("STRUCT: initial offset must be defined");
5344 if (currofs < 0 || currofs > 65535) fatal("STRUCT: invalid initial offset");
5347 // done with args
5348 if (!isLineEnd()) fatal("too many arguments for STRUCT");
5350 // for fast appending
5351 StructField *fldlast = (sth ? sth->fields : NULL);
5352 StructField *fldnew = NULL;
5354 if (fldlast) {
5355 while (fldlast->next) fldlast = fldlast->next;
5358 if (extend == 2) fldnew = sth->fields;
5360 // parse until ENDS
5361 stline = currSrcLine;
5362 nextSrcLine(); // skip ourself
5363 //fprintf(stderr, "001: <%s>\n", currLine);
5364 inum = -1;
5365 while (currSrcLine) {
5366 size_t slen = strlen(currLine);
5367 currLineRemoveComment();
5368 currLineTrimBlanks();
5369 if (!currLine[0]) { nextSrcLine(); continue; }
5371 if (strIsCommand("ENDS", currSrcLine->line)) break;
5372 //fprintf(stderr, "112: <%s> (%d)\n", currLine, inum);
5374 // parse new field
5375 int skipInit = 0;
5376 char *fldname = getLabelArg(0/*checkdelim*/);
5377 int fldsize = structCheckSizeDecl(fldname);
5378 if (fldsize == -666) {
5379 // defs/ds
5380 defined = 1;
5381 fldsize = getExprArg(&defined, NULL);
5382 if (!defined) fatal("STRUCT: DEFS size must be defined");
5383 if (fldsize < 0 || fldsize > 65535) fatal("STRUCT: DEFS size is invalid");
5384 skipInit = 1;
5385 fldname = NULL;
5386 } else if (fldsize < 0) {
5387 // this must be a label
5388 int found = 0;
5389 for (StructField *cf = sth->fields; cf; cf = cf->next) {
5390 if (cf->name && cf->name[0] && strcmp(cf->name, fldname) == 0) {
5391 found = 1;
5392 break;
5395 if (found) fatal("duplicate field name '%s'", fldname);
5396 fldname = strdup(fldname);
5397 char *szdecl = getLabelArg(0/*checkdelim*/);
5398 fldsize = structCheckSizeDecl(szdecl);
5399 if (fldsize == -666) {
5400 // defs/ds
5401 defined = 1;
5402 fldsize = getExprArg(&defined, NULL);
5403 if (!defined) fatal("STRUCT: DEFS size must be defined");
5404 if (fldsize < 0 || fldsize > 65535) fatal("STRUCT: DEFS size is invalid");
5405 skipInit = 1;
5406 } else {
5407 if (fldsize < 0) fatal("field size definition expected");
5409 } else {
5410 fldname = NULL; // no name
5413 // check for init value
5414 int hasInit = 0;
5415 int32_t initval = 0;
5417 if (!skipInit && checkDelim('=')) {
5418 defined = 1;
5419 initval = getExprArg(&defined, NULL);
5420 if (!defined) fatal("STRUCT: initial value must be defined");
5421 hasInit = 1;
5422 if (fldsize == 0 ||
5423 (fldsize == 1 && (initval < -128 || initval > 255)) ||
5424 (fldsize == 2 && (initval < -32768 || initval > 65535)))
5426 fatal("STRUCT: initial value is out of range");
5430 if (!isLineEnd()) fatal("STRUCT: extra field data");
5432 if (currofs+fldsize > 65535) fatal("STRUCT: too big");
5434 // create field
5435 StructField *fld = calloc(1, sizeof(StructField));
5436 fld->name = fldname;
5437 fld->ofs = currofs;
5438 fld->size = fldsize;
5439 fld->hasInit = hasInit;
5440 fld->initValue = initval;
5441 if (fldlast) fldlast->next = fld; else sth->fields = fld;
5442 fldlast = fld;
5443 currofs += fldsize;
5445 if (!fldnew) fldnew = fld;
5447 #if 0
5448 fprintf(stderr, "FLD <%s>: ofs=%u; size=%u; hasinit=%d; init=%d\n", fld->name, fld->ofs,
5449 fld->size, fld->hasInit, fld->initValue);
5450 #endif
5452 nextSrcLine();
5454 if (!currSrcLine) { setCurSrcLine(stline); fatal("no ENDS"); }
5455 if (currofs > 65535) fatal("STRUCT: too big");
5456 sth->size = currofs;
5458 //FIXME: structs must be local to module!
5460 // register new struct
5461 if (extend == 0 || extend == 2) {
5462 StructInfo *slast = structList;
5463 if (slast) {
5464 while (slast->next) slast = slast->next;
5465 slast->next = sth;
5466 } else {
5467 structList = sth;
5469 UrAsmOp *op = urAddOp(sth->name, &piSTRUCT_Internal);
5470 op->udata = sth;
5473 // add struct labels
5474 for (StructField *fld = fldnew; fld; fld = fld->next) {
5475 if (!fld->name || !fld->name[0]) continue;
5477 char *nn = mangleLabelName(sth->name);
5478 char *lbn = malloc(strlen(nn)+strlen(fld->name)+64);
5479 sprintf(lbn, "%s.%s", nn, fld->name);
5481 //fprintf(stderr, "%d: <%s>\n", extend, lbn);
5483 UrLabelInfo *c = urFindLabel(lbn);
5484 if (c) fatal("STRUCT: field name '%s' conflict!", fld->name);
5486 c = urAddLabel(lbn);
5487 free(lbn);
5488 c->type = LBL_TYPE_STOFS;
5489 c->value = fld->ofs;
5490 c->known = 1;
5493 // add `structname.sizeof`
5495 char *nn = mangleLabelName(sth->name);
5496 char *lbn = malloc(strlen(nn)+64);
5497 sprintf(lbn, "%s.%s", nn, "_sizeof");
5499 UrLabelInfo *c = urFindLabel(lbn);
5500 if (c) {
5501 if (!extend) fatal("STRUCT: field name '%s' conflict!", "_sizeof");
5502 } else {
5503 c = urAddLabel(lbn);
5505 free(lbn);
5507 c->type = LBL_TYPE_STOFS;
5508 c->value = sth->size;
5509 c->known = 1;
5512 return PI_SKIP_LINE;
5516 // ////////////////////////////////////////////////////////////////////////// //
5517 static int piEND_PASMO (void) {
5518 int defined = 1;
5519 int32_t res = getOneExprArg(&defined, NULL);
5520 //if (!defined) fatal("sorry, ENT operand value must be known here");
5521 if (res < 0 || res > 65535) fatal("invalid END operand value: %d", res);
5522 ent = res;
5523 return PI_CONT_LINE;
5527 ///////////////////////////////////////////////////////////////////////////////
5528 // DUP, EDUP
5530 static int piEDUP (void) {
5531 fatal("EDUP without DUP");
5532 checkOperatorEnd();
5533 return PI_SKIP_LINE;
5537 static int piENDR (void) {
5538 fatal("ENDR without REPT");
5539 checkOperatorEnd();
5540 return PI_SKIP_LINE;
5544 // DUP count [,var [,initval [,incr]]]
5545 static int piDUPEx (int isrept) {
5546 int defined = 1, inum;
5547 SourceLine *stline, *eline = NULL;
5549 //int32_t cnt = getOneExprArg(&defined, NULL);
5550 int32_t cnt = getExprArg(&defined, NULL);
5551 if (!defined) fatal("DUP: counter must be defined");
5552 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
5553 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
5555 int32_t initval = 0;
5556 int32_t incr = 1;
5558 // variable name
5559 char vname[128];
5560 vname[0] = 0;
5561 if (currLine[0] == ',') {
5562 (void)eatComma();
5563 char *lbl = getLabelArg(0/*checkdelim*/);
5564 if (lbl[0]) {
5565 if (strlen(lbl) > 127) fatal("DUP counter variable name too long");
5566 if (urFindOp(lbl) || !urasm_is_valid_name(lbl)) fatal("DUP counter variable name is invalid");
5567 strcpy(vname, lbl);
5571 // initial variable value
5572 if (currLine[0] == ',') {
5573 (void)eatComma();
5574 defined = 1;
5575 initval = getExprArg(&defined, NULL);
5576 if (!defined) fatal("DUP: initial value must be defined");
5579 // increment
5580 if (currLine[0] == ',') {
5581 (void)eatComma();
5582 defined = 1;
5583 incr = getExprArg(&defined, NULL);
5584 if (!defined) fatal("DUP: increment value must be defined");
5587 if (currLine[0]) fatal("too many arguments for DUP");
5589 int dupstack[128];
5590 int dupsp = 0;
5592 // now find corresponding EDUP
5593 // note that we should skip nested DUPs
5594 nextSrcLine(); // skip ourself
5595 stline = currSrcLine;
5596 while (currSrcLine) {
5597 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "REPT", "EDUP", "ENDR", "ENDM", NULL))) break;
5598 if (inum == 0 || inum == 1) {
5599 if (dupsp >= 128) fatal("too many nested DUPs");
5600 dupstack[dupsp++] = isrept;
5601 isrept = (inum == 1);
5602 nextSrcLine(); // skip DUP
5603 } else {
5604 // EDUP
5605 if (dupsp == 0) { eline = currSrcLine; break; }
5606 if (isrept) {
5607 if (inum < 3) fatal("invalid REPT end directive");
5608 } else {
5609 if (inum >= 3) fatal("invalid DUP end directive");
5611 isrept = dupstack[--dupsp];
5612 nextSrcLine(); // skip EDUP
5615 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
5617 // create counter local, if necessary
5618 UrLabelInfo *lcnt = (vname[0] ? urAddTempLocal(vname) : NULL);
5619 if (lcnt) lcnt->value = initval;
5621 // repeat lines
5622 while (cnt-- > 0) {
5623 setCurSrcLine(stline);
5624 while (currSrcLine != eline) processCurrentLine();
5625 // increment counter
5626 if (lcnt) lcnt->value += incr;
5629 // remove counter
5630 urRemoveTempLocal(lcnt);
5632 return PI_SKIP_LINE;
5636 static int piDUP (void) { return piDUPEx(0); }
5637 static int piREPT (void) { return piDUPEx(1); }
5640 ///////////////////////////////////////////////////////////////////////////////
5641 // IF, ENDIF
5643 static int ifCount = 0;
5646 // results:
5647 // -1: error (should not happen)
5648 // 0: successfully complete
5649 // 1: stopped *AT* "ELSE"
5650 // 2: stopped *AT* "ELSIF"
5651 static int ifSkipToEndIfOrElse (int wholeBody) {
5652 int inum, wasElse = 0;
5653 SourceLine *oline;
5655 while (currSrcLine) {
5656 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF",
5657 "ELSEIF", "MACRO", "ENDM", "IFX",
5658 "ELSEIFX",
5659 "ELSIF", "ELSIFX",
5660 NULL))) break;
5661 switch (inum) {
5662 case 0: /* if */
5663 case 6: /* ifx */
5664 nextSrcLine(); // skip IF
5665 ifSkipToEndIfOrElse(1); // and recurse
5666 nextSrcLine(); // skip ENDIF
5667 break;
5668 case 1: /* else */
5669 if (wasElse) fatal("duplicate ELSE");
5670 if (!wholeBody) return 1;
5671 wasElse = 1;
5672 nextSrcLine(); // skip ELSE
5673 break; // and continue
5674 case 2: /* endif */
5675 return 0; // and exit
5676 case 3: /* elseif */
5677 case 7: /* elseifx */
5678 case 8: /* elsif */
5679 case 9: /* elsifx */
5680 if (wasElse) fatal("ELSIF in ELSE");
5681 if (!wholeBody) return 2;
5682 nextSrcLine(); // skip ELSIF
5683 break; // and continue
5684 case 4: /* macro */
5685 // skip it as a whole
5686 nextSrcLine(); // skip MACRO
5687 for (;;) {
5688 oline = currSrcLine;
5689 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
5690 if (inum == 1) break;
5691 fatal("invalid nested MACRO");
5693 nextSrcLine(); // skip ENDM
5694 break;
5695 case 5: /* endm */
5696 fatal("unexpected ENDM");
5699 fatal("IF without ENDIF");
5700 return -1;
5704 static int piENDIF (void) {
5705 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5706 if (--ifCount < 0) fatal("ENDIF without IF");
5707 checkOperatorEnd();
5708 return PI_SKIP_LINE;
5712 static int piELSE (void) {
5713 if (--ifCount < 0) fatal("ELSE without IF");
5714 nextSrcLine(); // skip ELSE
5715 ifSkipToEndIfOrElse(1);
5716 return PI_SKIP_LINE;
5719 static int piELSIF (void) {
5720 if (--ifCount < 0) fatal("ELSIF without IF");
5721 nextSrcLine(); // skip ELSIF
5722 ifSkipToEndIfOrElse(1);
5723 return PI_SKIP_LINE;
5727 static int piELSIFX (void) {
5728 if (--ifCount < 0) fatal("ELSIFX without IF");
5729 nextSrcLine(); // skip ELSIFX
5730 ifSkipToEndIfOrElse(1);
5731 return PI_SKIP_LINE;
5735 static int piIFAll (int isIfX) {
5736 int defined = 1;
5737 int ooo = lblOptMakeU2;
5739 //fprintf(stderr, "piIFALL: <%s>\n", currLine);
5740 lblOptMakeU2 = (isIfX ? 1 : 0);
5741 int32_t cond = getOneExprArg(&defined, NULL);
5742 lblOptMakeU2 = ooo;
5743 //fprintf(stderr, "piIFALL COND: <%d>\n", cond);
5745 if (!defined) {
5746 if (!isIfX) fatal("IF: condition must be defined");
5747 cond = 0; // for IFX: 0 if there is any undefined label
5749 if (cond) {
5750 // ok, do it until ELSE/ELSIF/ENDIF
5751 ++ifCount;
5752 return PI_SKIP_LINE;
5754 for (;;) {
5755 int r;
5756 char *args;
5758 nextSrcLine(); // skip last instruction
5759 // skip until ELSE/ELSIF/ENDIF
5760 r = ifSkipToEndIfOrElse(0);
5761 /*!fprintf(stderr, "ELSIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5762 if (r == 0) break; // ENDIF
5763 if (r == 1) { ++ifCount; break; } // ELSE
5764 // ELSIF, do condition
5765 args = strIsCommand("ELSIF", currLine);
5766 if (args == NULL) args = strIsCommand("ELSEIF", currLine);
5767 if (args != NULL) {
5768 isIfX = 0;
5769 } else {
5770 args = strIsCommand("ELSIFX", currLine);
5771 if (args == NULL) args = strIsCommand("ELSEIFX", currLine);
5772 if (args == NULL) fatal("internal error in conditionals"); // the thing that should not be
5773 isIfX = 1;
5775 memmove(currLine, args, strlen(args)+1);
5776 /*!fprintf(stderr, "ELSIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5777 cond = getOneExprArg(&defined, NULL);
5778 /*!fprintf(stderr, "ELSIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0), cond);*/
5779 if (!defined) {
5780 if (!isIfX) fatal("ELSIF: condition must be defined");
5781 cond = 0; // for IFX: 0 if there is any undefined label
5783 if (cond) { ++ifCount; break; } // condition is true
5785 return PI_SKIP_LINE;
5789 static int piIF (void) { return piIFAll(0); }
5790 static int piIFX (void) { return piIFAll(1); }
5793 ///////////////////////////////////////////////////////////////////////////////
5794 // macro processor
5795 ///////////////////////////////////////////////////////////////////////////////
5797 what i did with MACRO is the brain-damaged cheating all the way.
5799 first, i will collect the MACRO body and remember it (removing it
5800 from the main source code, so second pass will not see it).
5802 second, when the macro is used, i will:
5803 * insert the whole macro body in place (label resolver will
5804 fix "..lbl" labels for us)
5805 * let the asm play with it
5807 this is not the best scheme, but it is fairly simple and it works.
5810 // will be called when parser encounters term starting with '=' or '*'
5811 // first term char will not be skipped
5812 // must return pointer to the first char after expression end
5813 static const char *getValueCB (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
5814 char name[257];
5815 char *p = name;
5817 if (curmacro == NULL) fatal("'=' outside of macro");
5818 if (*expr++ != '=') fatal("'=' expected!");
5820 expr = strSkipSpaces(expr);
5821 while (*expr) {
5822 if (isAlphaDigit(*expr) || *expr == '_') {
5823 if (p-name > 250) fatal("id too long");
5824 *p++ = *expr++;
5825 continue;
5827 break;
5829 *p = 0;
5831 expr = strSkipSpaces(expr);
5832 for (int f = 0; f < curmacro->mac->argc; ++f) {
5833 if (strEquCI(name, curmacro->mac->argnames[f])) {
5834 if (*expr == '[') {
5835 urasm_exprval_t v;
5836 int l = (int)strlen(curmacro->argvals[f]);
5837 ++expr; // skip "["
5838 urasm_exprval_init(&v);
5839 expr = urasm_expr_ex(&v, expr, addr, &donteval, defined, error);
5840 if (*error) return expr;
5841 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
5842 ++expr;
5843 if (v.val < 0) v.val += l;
5844 if (v.val < 0 || v.val >= l) {
5845 res->val = '?';
5846 } else {
5847 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
5849 return expr;
5850 } else {
5851 urasm_expr_ex(res, curmacro->argvals[f], addr, &donteval, defined, error);
5852 return expr;
5857 fatal("unknown macro variable: '%s'", name);
5861 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
5862 // opr starts with '=' (invariant)
5863 static int expandCB (char *opr, int oprlen) {
5864 char name[257], *p = name, *op = opr;
5866 if (curmacro == NULL) fatal("'=' outside of macro");
5867 if (*op++ != '=') fatal("'=' expected!"); // just in case
5868 //fprintf(stderr, "expand: [%s]\n", opr);
5870 if (!isAlpha(op[0])) return 0; // nothing to do
5872 // copy argument name
5873 while (*op) {
5874 if (isAlphaDigit(*op) || *op == '_') {
5875 if (p-name > 250) fatal("id too long");
5876 *p++ = *op++;
5877 continue;
5879 break;
5881 *p = 0;
5883 // expand argument? we only need to expand `=arg[n]`
5884 op = strSkipSpaces(op);
5885 if (op[0] != '[') return 0;
5887 for (int f = 0; f < curmacro->mac->argc; ++f) {
5888 if (strEquCI(name, curmacro->mac->argnames[f])) {
5889 ur_assert(*op == '[');
5890 // replace argument with character, or with literal value
5891 // used for `=regpair[0]` or `=regpair[]`
5892 if (op[1] == ']') {
5893 op += 2;
5894 const int l = (int)strlen(curmacro->argvals[f]);
5895 const int lleft = (int)strlen(op);
5896 char *tmp = malloc(l+lleft+8);
5897 snprintf(tmp, l+lleft+8, "%s%s", curmacro->argvals[f], op);
5898 if ((int)strlen(tmp) > oprlen) {
5899 free(tmp);
5900 return -1;
5902 strcpy(opr, tmp);
5903 free(tmp);
5904 return 0;
5905 } else {
5906 urasm_exprval_t v;
5907 int error = 0, defined = 1, donteval = 0;
5908 ++op; // skip '['
5909 urasm_exprval_init(&v);
5910 op = (char *)urasm_expr_ex(&v, op, pc, &donteval, &defined, &error);
5911 if (error) return -1;
5912 // result should be a number
5913 if (op == NULL || *op != ']' || v.str != NULL) return -1;
5914 ++op; // skip ']'
5915 // it is guaranteed to have more than one char in opr
5916 // so we can simply put char from argument value, and remove other expression chars
5917 const int l = (int)strlen(curmacro->argvals[f]);
5918 if (v.val < 0) v.val += l; // negative: indexing from the end
5919 if (v.val < 0 || v.val >= l) fatal("index %d is out of bounds for macro argument '%s'", v.val, curmacro->mac->argnames[f]);
5920 // copy char
5921 opr[0] = curmacro->argvals[f][v.val];
5922 // remove other chars
5923 memmove(opr+1, op, strlen(op)+1);
5925 return 0;
5929 fatal("unknown macro variable: '%s'", name);
5933 // main macro expander
5934 static void processMacro (MacroDef *mc) {
5935 SourceLine *oldcurline = currSrcLine;
5936 CurMacroDef cm;
5937 memset(&cm, 0, sizeof(cm));
5939 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
5940 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
5942 // parse macro arguments
5943 cm.mac = mc;
5944 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) cm.argvals[f] = NULL;
5945 int currArg = 0;
5946 for (;;) {
5947 removeSpaces();
5948 // do we have more arguments?
5949 if (!currLine[0]) break;
5950 // check for argument name (this is purely cosmetic thing)
5951 // use like this: "mcall :argname=[value]" (value may be omited for default)
5952 if (currLine[0] == ':' && isAlpha(currLine[1])) {
5953 int pos = 2;
5954 while (isAlphaDigit(currLine[pos]) || currLine[pos] == '_') ++pos;
5955 //hack!
5956 const char svch = currLine[pos];
5957 currLine[pos] = 0;
5958 if (!strEquCI(currLine+1, mc->argnames[currArg])) {
5959 // out-of-order, find proper argument and rewind to it
5960 currArg = -1;
5961 for (int c = 0; c < mc->argc; ++c) {
5962 if (strEquCI(currLine+1, mc->argnames[c])) {
5963 currArg = c;
5964 break;
5967 if (currArg < 0) fatal("macro '%s' has no argument named '%s'", mc->name, currLine+1);
5969 currLine[pos] = svch;
5970 // remove argument name
5971 memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
5972 removeSpaces();
5973 // check for '='
5974 if (currLine[0] != '=') fatal("expected '=' after argument name");
5975 // remove '='
5976 memmove(currLine, currLine+1, strlen(currLine));
5977 removeSpaces();
5979 // too many args?
5980 if (currArg >= mc->argc) fatal("too many arguments to macro '%s'", mc->name);
5981 // check for default value
5982 if (currLine[0] == ',') {
5983 if (mc->argdefaults[currArg] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[currArg]);
5984 cm.argvals[currArg] = strdup(mc->argdefaults[currArg]);
5985 memmove(currLine, currLine+1, strlen(currLine));
5986 } else {
5987 // skip argument (so we will know its length, and will be able to copy it)
5988 char *e = skipMacroArg(currLine);
5989 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
5990 const char ech = *e;
5991 if (ech != 0) {
5992 if (ech == ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc->name);
5993 if (ech != ',') fatal("invalid invocation of macro '%s'", mc->name);
5995 *e = 0;
5996 cm.argvals[currArg] = strdup(currLine);
5997 // strip trailing spaces
5998 strTrimRight(cm.argvals[currArg]);
5999 if (ech) {
6000 *e = ech;
6001 memmove(currLine, e+1, strlen(e));
6002 } else {
6003 currLine[0] = 0;
6006 ++currArg;
6008 // check for line end
6009 removeSpaces();
6010 if (currLine[0]) fatal("invalid macro invocation");
6012 // setup default argument values
6013 for (int f = 0; f < mc->argc; ++f) {
6014 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
6015 if (cm.argvals[f]) continue;
6016 if (mc->argdefaults[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
6017 cm.argvals[f] = strdup(mc->argdefaults[f]);
6018 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
6021 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
6023 // insert macro code into the source
6024 setCurSrcLine(mc->lines);
6025 curmacro = &cm;
6026 ++curmacronum;
6027 while (currSrcLine != NULL) {
6028 //fprintf(stderr, "*[%s] (%d)\n", currSrcLine->line, currSrcLine->lineNo);
6029 processCurrentLine();
6032 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) if (cm.argvals[f]) free(cm.argvals[f]);
6033 setCurSrcLine(oldcurline);
6034 curmacro = NULL;
6035 nextSrcLine();
6039 static int piMACRO (void) {
6040 char *name;
6041 int argc = 0;
6042 char *argdefaults[MAX_MACRO_ARGS];
6043 char *argnames[MAX_MACRO_ARGS];
6044 SourceLine *stline, *eline = NULL;
6045 MacroDef *mc;
6047 name = strdup(getLabelArg(0));
6048 //fprintf(stderr, "[%s]\n", name);
6049 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
6050 if (currLine[0] == ',') memmove(currLine, currLine+1, strlen(currLine));
6051 removeSpaces();
6053 while (currLine[0]) {
6054 if (argc >= MAX_MACRO_ARGS) fatal("too many arguments in MACRO");
6055 if (!isAlpha(currLine[0])) fatal("invalid MACRO definition");
6056 argnames[argc] = strdup(getLabelArg(0));
6057 if (currLine[0] == '=') {
6058 // default value
6059 char *e = strchr(currLine, ','), tch;
6061 if (e == NULL) e = currLine+strlen(currLine);
6062 tch = *e;
6063 *e = 0;
6064 argdefaults[argc] = strdup(currLine+1);
6065 *e = tch;
6066 memmove(currLine, e, strlen(e)+1);
6067 } else {
6068 argdefaults[argc] = NULL;
6070 removeSpaces();
6071 if (currLine[0] == ',') {
6072 memmove(currLine, currLine+1, strlen(currLine));
6073 removeSpaces();
6075 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
6076 ++argc;
6079 // now find corresponding ENDM
6080 // note that we should skip nested DUPs
6081 stline = currSrcLine;
6082 nextSrcLine(); // skip ourself
6083 while (currSrcLine) {
6084 int inum;
6086 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
6087 // ok, we found something; what is it?
6088 if (inum == 0) {
6089 // new MACRO
6090 fatal("no nested MACROs yet");
6091 } else {
6092 // ENDM, gotcha!
6093 eline = currSrcLine;
6094 // kill ENDM
6095 eline->line[0] = 0;
6096 nextSrcLine(); // skip ENDM
6097 break;
6100 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
6102 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
6103 mc->name = name;
6104 mc->argc = argc;
6105 for (int f = 0; f < argc; ++f) { mc->argdefaults[f] = argdefaults[f]; mc->argnames[f] = argnames[f]; }
6107 eline->next = NULL;
6108 mc->lines = stline->next;
6109 stline->next = currSrcLine;
6110 stline->line[0] = 0;
6112 mc->next = maclist;
6113 maclist = mc;
6114 setCurSrcLine(stline);
6115 return PI_SKIP_LINE;
6119 static int piENDM (void) {
6120 fatal("ENDM without MACRO");
6121 return PI_SKIP_LINE;
6125 ///////////////////////////////////////////////////////////////////////////////
6126 // line processor
6127 static void piTapParseLoaderName (void) {
6128 if (eatComma()) {
6129 int len;
6130 if (!isStrArg()) fatal("loader name expected");
6131 char *fn = getStrArg(&len);
6132 if (len > 10) fatal("loader name too long");
6133 memset(tapeLoaderName, ' ', 10);
6134 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
6139 static int piMATHMODE (void) {
6140 char *name = getOneLabelArg();
6141 if (strEquCI(name, "OLD")) urasm_use_old_priorities = 1;
6142 else if (strEquCI(name, "NEW")) urasm_use_old_priorities = 0;
6143 else fatal("invalid math mode; NEW or OLD expected");
6144 return PI_SKIP_LINE;
6148 static int piCASTRATES (void) {
6149 char *name = getOneLabelArg();
6150 if (strEquCI(name, "YES")) urasm_allow_hex_castrates = 1;
6151 else if (strEquCI(name, "TAN")) urasm_allow_hex_castrates = 1;
6152 else if (strEquCI(name, "NO")) urasm_allow_hex_castrates = 0;
6153 else if (strEquCI(name, "ONA")) urasm_allow_hex_castrates = 0;
6154 else fatal("yes/no value expected");
6155 return PI_SKIP_LINE;
6159 static int piREFOPT (void) {
6160 char *name = getOneLabelArg();
6161 if (strEquCI(name, "ALLLABELS")) urasm_dump_all_labels = 1;
6162 else if (strEquCI(name, "ALL_LABELS")) urasm_dump_all_labels = 1;
6163 else if (strEquCI(name, "NOUPCASE")) urasm_dump_all_labels = 0;
6164 else if (strEquCI(name, "NO_UPCASE")) urasm_dump_all_labels = 0;
6165 else if (strEquCI(name, "DEFAULT")) urasm_dump_all_labels = 1;
6166 else fatal("invalid refopt mode");
6167 return PI_SKIP_LINE;
6171 static int piDEFFMT (void) {
6172 char *name;
6174 if (isStrArg()) {
6175 int len = 0;
6176 name = getStrArg(&len);
6177 } else {
6178 name = getLabelArg(1);
6180 if (optWTChanged) return 1;
6182 optRunDMB = optRunTape = optRunSCL = optRunDSK = 0;
6183 //optRunSNA = 0;
6185 if (strEquCI(name, "SNA") || strEquCI(name, "RUNSNA") || strEquCI(name, "SNARUN")) {
6186 optWriteType = 's';
6187 if (currLine[0]) fatal("too many expressions");
6188 return PI_SKIP_LINE;
6190 if (strEquCI(name, "TAP") || strEquCI(name, "TAPE")) {
6191 optWriteType = 't';
6192 piTapParseLoaderName();
6193 return PI_SKIP_LINE;
6195 if (strEquCI(name, "RUNTAP") || strEquCI(name, "RUNTAPE") || strEquCI(name, "TAPERUN")) {
6196 optRunTape = 1;
6197 optWriteType = 't';
6198 piTapParseLoaderName();
6199 return PI_SKIP_LINE;
6201 if (strEquCI(name, "BIN") || strEquCI(name, "RAW")) {
6202 optWriteType = 'r';
6203 if (currLine[0]) fatal("too many expressions");
6204 return PI_SKIP_LINE;
6206 if (strEquCI(name, "DMB") || strEquCI(name, "RUNDMB") || strEquCI(name, "DMBRUN")) {
6207 optRunDMB = (name[3] != 0);
6208 optWriteType = 'd';
6209 if (currLine[0]) fatal("too many expressions");
6210 return PI_SKIP_LINE;
6212 if (strEquCI(name, "NONE") || strEquCI(name, "NOTHING")) {
6213 optWriteType = 'n';
6214 if (currLine[0]) fatal("too many expressions");
6215 return PI_SKIP_LINE;
6217 if (strEquCI(name, "SCL") || strEquCI(name, "RUNSCL") || strEquCI(name, "SCLRUN")) {
6218 optWriteType = 'S';
6219 optRunSCL = (name[3] != 0 ? -1 : 0); /* no boot */
6220 piTapParseLoaderName();
6221 return PI_SKIP_LINE;
6223 if (strEquCI(name, "SCLMONO") || strEquCI(name, "RUNSCLMONO") || strEquCI(name, "SCLMONORUN")) {
6224 optWriteType = 'M';
6225 optRunSCL = (name[7] != 0 ? -1 : 0); /* no boot */
6226 piTapParseLoaderName();
6227 return PI_SKIP_LINE;
6229 if (strEquCI(name, "SCLBOOT") || strEquCI(name, "BOOTSCL")) {
6230 optWriteType = 'S';
6231 optRunSCL = 1; /* with boot */
6232 piTapParseLoaderName();
6233 return PI_SKIP_LINE;
6235 if (strEquCI(name, "SCLMONOBOOT") || strEquCI(name, "BOOTSCLMONO")) {
6236 optWriteType = 'M';
6237 optRunSCL = 1; /* with boot */
6238 piTapParseLoaderName();
6239 return PI_SKIP_LINE;
6242 if (strEquCI(name, "DSKBOOT") || strEquCI(name, "BOOTDSK") ||
6243 strEquCI(name, "DSK180BOOT") || strEquCI(name, "BOOTDSK180"))
6245 optWriteType = '3';
6246 optRunDSK = 1; /* with boot */
6247 optDskType = P3DSK_PCW_SS; // +3DOS 180K disk
6248 piTapParseLoaderName();
6249 return PI_SKIP_LINE;
6252 if (strEquCI(name, "DSK720BOOT") || strEquCI(name, "BOOTDSK720")) {
6253 optWriteType = '3';
6254 optRunDSK = 1; /* with boot */
6255 optDskType = P3DSK_PCW_DS; // +3DOS 720K disk
6256 piTapParseLoaderName();
6257 return PI_SKIP_LINE;
6260 if (strEquCI(name, "DSK") || strEquCI(name, "DSK180")) {
6261 optWriteType = '3';
6262 optRunDSK = 0; /* without boot */
6263 optDskType = P3DSK_PCW_SS; // +3DOS 180K disk
6264 piTapParseLoaderName();
6265 return PI_SKIP_LINE;
6268 if (strEquCI(name, "DSK720")) {
6269 optWriteType = '3';
6270 optRunDSK = 0; /* without boot */
6271 optDskType = P3DSK_PCW_DS; // +3DOS 720K disk
6272 piTapParseLoaderName();
6273 return PI_SKIP_LINE;
6276 fatal("invalid default output type: %s", name);
6280 ///////////////////////////////////////////////////////////////////////////////
6281 // line processor
6283 static void processCurrentLine (void) {
6284 if (!currSrcLine) return; // do nothing
6285 loadCurSrcLine();
6286 ur_assert(asmMode != AMODE_UFO); // the thing that should not be
6287 //fprintf(stderr, "<%s>\n", currLine);
6288 processLabel();
6289 for (;;) {
6290 char *str, *ee, name[66];
6291 UrAsmOp *op;
6292 int len;
6293 const char *errpos;
6294 removeSpacesAndColons();
6295 // skip spaces and ':'
6296 if (!currLine[0]) { nextSrcLine(); break; }
6297 // try to find and process command
6298 str = currLine; //while (*str && isSpace(*str)) ++str; // skip spaces
6299 // find command end
6300 for (ee = str; *ee && !isSpace(*ee) && *ee != '"' && *ee != '\'' && *ee != ',' && *ee != ':'; ++ee) {}
6301 // get command, if any
6302 if (ee != str && ee-str <= 64) {
6303 MacroDef *mc;
6304 memset(name, 0, sizeof(name));
6305 memmove(name, str, ee-str);
6306 /* UrForth macro? */
6307 uint32_t fwmacro = ufoFindWordInVocabulary(name, ufoMacroVocId);
6308 if (fwmacro != 0) {
6309 // ok, do it
6310 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6311 memmove(currLine, str, strlen(str)+1);
6312 ufoRunMacroWord(fwmacro);
6313 /* only one macro per line! */
6314 nextSrcLine(); // skip it
6315 break;
6317 /* known command? */
6318 op = urFindOp(name);
6319 if (op) {
6320 // ok, do it
6321 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6322 memmove(currLine, str, strlen(str)+1);
6323 urCurrentOp = op;
6324 if (op->fn()) {
6325 urCurrentOp = NULL;
6326 nextSrcLine(); // skip it
6327 break;
6329 urCurrentOp = NULL;
6330 continue;
6332 /* known macro? */
6333 if ((mc = findMacro(name)) != NULL) {
6334 // ok, do it
6335 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6336 memmove(currLine, str, strlen(str)+1);
6337 processMacro(mc);
6338 /* only one macro per line! */
6339 break;
6342 // with reservation management
6343 again_from_reserve:
6344 len = urasm_opasm(currLine, pc, disp, &errpos);
6345 if (len < 0) fatalUrLib(len);
6346 // check for reservation
6347 if (len > 0) {
6348 if (reserveHit(pc, len)) {
6349 insert_jump:
6350 if (pc != disp) fatal("trying to use reserved areas with phased code");
6351 reserveReleaseUsed(pc, len);
6352 int nextaddr = reservedFindNextSuitableFreeFrom(pc, pc+len);
6353 if (nextaddr < 0) fatal("codegen started in reserved area #%04X", pc);
6354 // insert jump to the new area, and retry
6355 fprintf(stderr, "...inserting jump from #%04X to #%04X\n", (unsigned)pc, (unsigned)nextaddr);
6356 // can we insert a JP? it is faster than JR
6357 if (memresv[pc] || memresv[pc+1]) fatal("internal reserve area manager error at #%04X", pc);
6358 if (!memresv[pc+2]) {
6359 // use JP
6360 emitByte(0xc3U); // JP
6361 emitWord((uint16_t)nextaddr);
6362 } else {
6363 // use JR
6364 int jrdest = nextaddr-(pc+2U);
6365 if (jrdest >= 128) fatal("internal reserve area manager error at #%04X (jr dist)", pc);
6366 emitByte(0x18U); // JR
6367 emitByte((uint8_t)jrdest);
6369 disp = pc = (uint16_t)nextaddr;
6370 goto again_from_reserve;
6371 } else {
6372 // did we compiled JP/JR?
6373 if (getByte(pc) == 0xc3U || getByte(pc) == 0x18U) {
6374 // no room for jump?
6375 if (memresv[(pc+len)&0xffffU] || memresv[(pc+len+1)&0xffffU] || memresv[(pc+len+2)&0xffffU]) {
6376 if (pc != disp) fatal("trying to use reserved areas with phased code");
6377 // find next free area
6378 int next = pc+len;
6379 while (next >= 0 && next < 65536) {
6380 next = reserveFindFreeFrom(next);
6381 if (next < 0) break;
6382 // has room for JP there?
6383 if (!memresv[next] && !memresv[(next+1)&0xffffU] && !memresv[(next+2)&0xffffU]) break;
6384 // no room for JP, has room for JP?
6385 if (!memresv[next] && !memresv[(next+1)&0xffffU]) {
6386 // check if jr can fit
6387 const int nj = reservedFindNextJumpableFreeFrom(next, next+2);
6388 if (nj >= 0) break;
6390 next = reserveFindReservedFrom(next);
6392 if (next >= 0) disp = pc = (next-len)&0xffffU; // so "+len" will make PC right
6394 } else {
6395 // check if we still have enough room for a possible jp/jr
6396 if (memresv[(pc+len)&0xffffU] || memresv[(pc+len+1)&0xffffU]) goto insert_jump;
6397 // we have at least a room for JR, check if we have a room for JP
6398 if (!memresv[(pc+len+2)&0xffffU]) {
6399 // no room for JP, check if jr will be enough
6400 int next = reservedFindNextSuitableFreeFrom(pc, pc+len+2);
6401 if (next >= 0 && next-(pc+len+2u) >= 128) goto insert_jump;
6406 pc += len;
6407 disp += len;
6408 if (len >= 0 && errpos) {
6409 memmove(currLine, errpos+1, strlen(errpos));
6410 } else {
6411 nextSrcLine(); // skip it
6412 break;
6418 ///////////////////////////////////////////////////////////////////////////////
6419 // start inline UrForth code
6421 static int piSTARTFORTH (void) {
6422 checkOperatorEnd();
6423 asmMode = AMODE_UFO;
6424 ufoRunInterpretLoop();
6425 asmMode = AMODE_NORMAL;
6426 return PI_SKIP_LINE;
6430 ///////////////////////////////////////////////////////////////////////////////
6431 // setup instructions
6433 static void registerInstructions (void) {
6434 urAddOpAndDollar("DISPLAY", piDISPLAY);
6435 urAddOpAndDollar("DISPLAY0", piDISPLAY0);
6436 urAddOpAndDollar("DISPLAYA", piDISPLAYA);
6437 urAddOpAndDollar("DISPHEX", piDISPHEX);
6438 urAddOpAndDollar("DISPHEX0", piDISPHEX0);
6439 urAddOpAndDollar("DISPHEXA", piDISPHEXA);
6441 urAddOpAndDollar("DEFFMT", piDEFFMT);
6442 urAddOp("$DEFFMT", piDEFFMT);
6443 urAddOp("$MODEL", piMODEL);
6444 urAddOp("$SAVECODE", piSAVECODE);
6446 urAddOp("MACRO", piMACRO);
6447 urAddOp("ENDM", piENDM);
6449 urAddOp("ORG", piORG);
6450 urAddOp("DISP", piDISP);
6451 urAddOp("ENDDISP", piENDDISP);
6452 urAddOp("PHASE", piDISP);
6453 urAddOp("DEPHASE", piENDDISP);
6454 urAddOp("UNPHASE", piENDDISP);
6455 urAddOp("ALIGN", piALIGN);
6456 urAddOp("DISPALIGN", piDISPALIGN);
6457 urAddOp("PHASEALIGN", piDISPALIGN);
6458 urAddOp("ENT", piENT);
6459 urAddOp("CLR", piCLR);
6460 urAddOp("RESERVE", piRESERVE);
6462 urAddOpAndDollar("INCLUDE", piINCLUDE);
6463 urAddOpAndDollar("INCBIN", piINCBIN);
6464 urAddOpAndDollar("DATABIN", piDATABIN);
6465 urAddOpAndDollar("CODEBIN", piCODEBIN);
6467 urAddOp("MODULE", piMODULE);
6468 urAddOp("ENDMODULE", piENDMODULE);
6470 urAddOp("DUP", piDUP);
6471 urAddOp("EDUP", piEDUP);
6472 urAddOp("REPT", piREPT);
6473 urAddOp("ENDR", piENDR);
6474 // structures
6475 urAddOp("STRUCT", piSTRUCT);
6476 urAddOp("ENDS", piENDS);
6477 // pasmo support
6478 urAddOp("END", piEND_PASMO);
6480 urAddOpAndDollar("IF", piIF);
6481 urAddOpAndDollar("IFX", piIFX);
6482 urAddOpAndDollar("ELSE", piELSE);
6483 urAddOpAndDollar("ELSIF", piELSIF);
6484 urAddOpAndDollar("ELSIFX", piELSIFX);
6485 urAddOpAndDollar("ELSEIF", piELSIF);
6486 urAddOpAndDollar("ELSEIFX", piELSIFX);
6487 urAddOpAndDollar("ENDIF", piENDIF);
6489 urAddOp("DEFINCR", piDEFINCR);
6490 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
6491 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
6492 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
6493 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
6494 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
6495 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
6496 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
6497 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
6499 urAddOp("$ERROR", piERROR);
6500 urAddOp("$WARNING", piWARNING);
6502 urAddOp("$PRINTF", piPRINTF);
6503 urAddOp("$PRINTF0", piPRINTF0);
6504 urAddOp("$PRINTFA", piPRINTFA);
6506 urAddOp("$MATHMODE", piMATHMODE);
6507 urAddOp("$REFOPT", piREFOPT);
6508 urAddOp("$CASTRATES", piCASTRATES);
6510 urAddOp("$START_FORTH", piSTARTFORTH);
6514 ///////////////////////////////////////////////////////////////////////////////
6515 // !0: invalid label
6517 static inline void fnSkipSpaces (const char *expr) {
6518 while (*expr && isSpace(*expr)) ++expr;
6519 return expr;
6524 UR_FORCE_INLINE char fnNextChar (const char *expr) {
6525 while (*expr && isSpace(*expr)) ++expr;
6526 return *expr;
6530 #define FN_SKIP_BLANKS do { \
6531 while (*expr && isSpace(*expr)) ++expr; \
6532 } while (0)
6534 #define FN_CHECK_END do { \
6535 while (*expr && isSpace(*expr)) ++expr; \
6536 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
6537 ++expr; \
6538 } while (0)
6541 #define FN_CHECK_COMMA do { \
6542 while (*expr && isSpace(*expr)) ++expr; \
6543 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
6544 ++expr; \
6545 } while (0)
6548 static const char *readLabelName (char *buf, const char *expr) {
6549 int pos = 0;
6551 while (*expr && isSpace(*expr)) ++expr;
6552 for (;;) {
6553 char ch = *expr++;
6555 if (pos >= 128) return NULL;
6556 if (ch == ')') { --expr; break; }
6557 if (!ch) break;
6558 if (isAlphaDigit(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
6559 buf[pos++] = ch;
6560 } else {
6561 break;
6564 if (pos < 1) return NULL;
6565 buf[pos] = '\0';
6566 if (!urasm_is_valid_name(buf)) return NULL;
6567 return expr;
6571 static const char *fnDefKn (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
6572 char lbl[130];
6574 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
6575 FN_CHECK_END;
6576 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
6577 return expr;
6580 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); }
6581 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); }
6584 static const char *fnAligned256 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6585 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6586 FN_CHECK_END;
6587 if (!donteval) res->val = (res->val%256 ? 0 : 1);
6588 return expr;
6592 static const char *fnSameSeg (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6593 urasm_exprval_t v0, v1;
6595 urasm_exprval_init(&v0);
6596 urasm_exprval_init(&v1);
6597 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
6598 if (*error) return expr;
6599 FN_CHECK_COMMA;
6600 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
6601 if (*error) return expr;
6602 FN_CHECK_END;
6603 if (!donteval) res->val = (v0.val/256 == v1.val/256);
6604 return expr;
6608 static const char *fnAlign (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6609 urasm_exprval_t v0, v1;
6610 urasm_exprval_init(&v0);
6611 urasm_exprval_init(&v1);
6612 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
6613 if (*error) return expr;
6614 FN_SKIP_BLANKS;
6615 if (fnNextChar(expr) == ',') {
6616 FN_CHECK_COMMA;
6617 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
6618 if (*error) return expr;
6619 } else {
6620 v1.val = 256;
6622 FN_CHECK_END;
6623 if (!donteval) {
6624 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
6625 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
6627 return expr;
6631 static const char *fnLow (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6632 const char *ee = expr;
6633 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6634 FN_CHECK_END;
6635 if (!donteval) {
6636 if (res->fixuptype == UR_FIXUP_HIBYTE) {
6637 *error = UR_EXPRERR_FUNC; return ee;
6639 res->fixuptype = UR_FIXUP_LOBYTE;
6640 res->val &= 0xff;
6642 return expr;
6646 static const char *fnHigh (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6647 const char *ee = expr;
6648 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6649 FN_CHECK_END;
6650 if (!donteval) {
6651 if (res->fixuptype == UR_FIXUP_LOBYTE) {
6652 *error = UR_EXPRERR_FUNC; return ee;
6654 res->fixuptype = UR_FIXUP_HIBYTE;
6655 res->val = (res->val>>8)&0xff;
6657 return expr;
6661 static const char *fnAbs (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6662 const char *ee = expr;
6663 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6664 FN_CHECK_END;
6665 if (!donteval) {
6666 if (res->fixuptype != UR_FIXUP_NONE) {
6667 *error = UR_EXPRERR_FUNC; return ee;
6669 res->val = abs(res->val);
6671 return expr;
6675 static const char *fnBSwap (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6676 const char *ee = expr;
6677 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6678 FN_CHECK_END;
6679 if (!donteval) {
6680 if (res->fixuptype != UR_FIXUP_NONE) {
6681 *error = UR_EXPRERR_FUNC; return ee;
6683 res->val = ((res->val>>8)&0xff)|((res->val&0xff)<<8);
6685 return expr;
6689 static const char *fnScrAddr8xn (int nmul, urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6690 int32_t scrbase = 0x4000;
6691 int32_t x, y;
6692 const char *ee = expr;
6693 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
6694 // first argument is `x`
6695 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6696 x = res->val;
6697 // second argument is `y`
6698 FN_CHECK_COMMA;
6699 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6700 y = res->val*nmul;
6701 // optional third arg is screen base
6702 FN_SKIP_BLANKS;
6703 if (expr[0] == ',') {
6704 FN_CHECK_COMMA;
6705 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6706 scrbase = res->val&0xffff;
6708 FN_CHECK_END;
6709 if (!donteval) {
6710 //urasm_exprval_clear(res);
6711 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
6712 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
6713 if (y < 0 || y > 191) fatal("invalid y coordinate: %d", y/nmul);
6714 res->val = scrbase+
6715 (y/64)*2048+
6716 (y%8)*256+
6717 ((y%64)/8)*32+
6719 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
6721 return expr;
6725 static const char *fnScrAddr8x1 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6726 return fnScrAddr8xn(1, res, expr, addr, donteval, defined, error);
6729 static const char *fnScrAddr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6730 return fnScrAddr8xn(8, res, expr, addr, donteval, defined, error);
6734 static const char *fnScrAttr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6735 int32_t scrbase = 0x4000;
6736 int32_t x, y;
6737 const char *ee = expr;
6738 // first argument is `x`
6739 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6740 x = res->val;
6741 // second argument is `y`
6742 FN_CHECK_COMMA;
6743 urasm_exprval_clear(res);
6744 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6745 y = res->val;
6746 // optional third arg is screen base
6747 FN_SKIP_BLANKS;
6748 if (expr[0] == ',') {
6749 FN_CHECK_COMMA;
6750 urasm_exprval_clear(res);
6751 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6752 scrbase = res->val&0xffff;
6754 urasm_exprval_clear(res);
6755 FN_CHECK_END;
6756 if (!donteval) {
6757 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
6758 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
6759 if (y < 0 || y > 23) fatal("invalid y coordinate: %d", y);
6760 res->val = scrbase+6144+y*32+x;
6762 return expr;
6766 static const char *fnMArgToStr (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6767 const char *ee = expr;
6768 // argument is macro argument name
6769 expr = strSkipSpacesConst(expr);
6770 if (expr[0] != '=') { *error = UR_EXPRERR_FUNC; return ee; }
6771 ++expr;
6772 if (!isAlpha(expr[0]) && expr[0] != '_') { *error = UR_EXPRERR_FUNC; return ee; }
6773 const char *nend = expr+1;
6774 while (isAlphaDigit(*nend) || *nend == '_') ++nend;
6775 if (nend-expr > 127) { *error = UR_EXPRERR_FUNC; return ee; }
6776 char name[128];
6777 memset(name, 0, sizeof(name));
6778 memcpy(name, expr, nend-expr);
6779 expr = nend;
6780 // parse index
6781 int index = 0;
6782 int has_index = 0;
6783 expr = strSkipSpacesConst(expr);
6784 if (expr[0] == '[') {
6785 expr = strSkipSpacesConst(expr+1);
6786 if (expr[0] != ']') {
6787 has_index = 1;
6788 int doneg = 0;
6789 if (expr[0] == '+') ++expr;
6790 else if (expr[0] == '-') { doneg = 1; ++expr; }
6791 index = 0;
6792 while (isDigit(expr[0])) {
6793 index = index*10+(expr[0]-'0');
6794 if (index >= 0x1fffffff) fatal("`margtostr` index too high");
6795 ++expr;
6797 expr = strSkipSpacesConst(expr);
6798 if (doneg) index = -index;
6800 if (expr[0] != ']') fatal("`margtostr` invalid index syntax");
6801 ++expr;
6803 // done
6804 FN_CHECK_END;
6805 if (curmacro == NULL) fatal("`margtostr` outside of macro");
6806 if (!donteval) {
6807 for (int f = 0; f < curmacro->mac->argc; ++f) {
6808 if (strEquCI(name, curmacro->mac->argnames[f])) {
6809 // found argument, convert it to string
6810 if (!has_index) {
6811 res->str = realloc(res->str, strlen(curmacro->argvals[f])+1);
6812 strcpy(res->str, curmacro->argvals[f]);
6813 } else {
6814 const size_t slen = strlen(curmacro->argvals[f]);
6815 if (index < 0) {
6816 index = -index;
6817 if (index > slen) fatal("index out of string");
6818 index = (int)slen-index;
6820 if (index >= slen) fatal("index out of string");
6821 res->str = realloc(res->str, 2);
6822 res->str[0] = curmacro->argvals[f][index];
6823 res->str[1] = 0;
6825 // lowercase it
6826 for (char *s = res->str; *s; ++s) *s = toLower(*s);
6827 //fprintf(stderr, "<%s>\n", res->str);
6828 if (res->str[0]) {
6829 if (res->str[1]) {
6830 res->val = ((unsigned char)res->str[0]);
6831 res->val |= ((unsigned char)res->str[1])<<8;
6832 } else {
6833 res->val = (unsigned char)res->str[0];
6835 } else {
6836 res->val = 0;
6838 return expr;
6841 fatal("unknown macro argument '%s'", name);
6843 return expr;
6847 static const char *fnStrLen (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6848 expr = strSkipSpacesConst(expr);
6849 int stlen = 0;
6850 switch (expr[0]) {
6851 case '"': case '\'': /* string literal? */
6853 char *a = (char *)expr+1;
6854 (void)parseStr(&a, expr[0], &stlen);
6855 expr = (const char *)a;
6857 break;
6858 default: /* expression */
6860 urasm_exprval_t r;
6861 urasm_exprval_init(&r);
6862 expr = urasm_expr_ex(&r, expr, disp, &donteval, defined, error);
6863 if (*error) fatalUrLib(*error);
6864 if (!r.str) fatal("string expected for `strlen()`");
6865 stlen = (int)strlen(r.str);
6866 urasm_exprval_clear(&r);
6868 break;
6870 FN_CHECK_END;
6871 if (!donteval) {
6872 urasm_exprval_setint(res, stlen);
6874 return expr;
6878 static void registerFunctions (void) {
6879 urasm_expr_register_func("defined", fnDefined);
6880 urasm_expr_register_func("known", fnKnown);
6881 urasm_expr_register_func("aligned256", fnAligned256);
6882 urasm_expr_register_func("align", fnAlign);
6883 urasm_expr_register_func("sameseg", fnSameSeg);
6884 urasm_expr_register_func("low", fnLow);
6885 urasm_expr_register_func("high", fnHigh);
6886 urasm_expr_register_func("abs", fnAbs);
6887 urasm_expr_register_func("bswap", fnBSwap);
6889 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8);
6890 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1);
6891 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8);
6893 urasm_expr_register_func("marg2str", fnMArgToStr);
6894 urasm_expr_register_func("strlen", fnStrLen);
6898 ///////////////////////////////////////////////////////////////////////////////
6899 // preparing another pass
6901 static void initPass (void) {
6902 asmMode = AMODE_NORMAL;
6903 currSrcLine = asmText;
6904 currModule = NULL;
6905 pc = start_pc;
6906 disp = start_disp;
6907 ent = start_ent;
6908 inTapeBlock = 0;
6909 tapeXorB = 0;
6910 wasOrg = 0;
6911 wasClr = 0;
6912 ifCount = 0;
6913 dfi_free();
6914 //defIncr = 0;
6915 lblOptMakeU2 = 0;
6916 curmacronum = 0;
6917 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
6918 lastSeenGlobalLabel = NULL;
6919 //lastSeenGlobalLabel = strdup(" [MAIN] ");
6920 modulesResetSeen();
6921 prepareMemory();
6922 urasm_use_old_priorities = 0;
6923 strcpy(lastFindLabelName, "");
6924 strcpy(currSeenLabel, "");
6925 urResetTempLabels();
6926 ufoClearIncludePath();
6930 static int postPass (void) {
6931 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
6932 lastSeenGlobalLabel = NULL;
6933 if (checkLabels()) return -1;
6934 if (ifCount != 0) fatal("unbalanced IFs");
6935 return 0;
6939 ///////////////////////////////////////////////////////////////////////////////
6940 static int labelCmp (const void *aa, const void *bb) {
6941 if (aa == bb) return 0;
6942 const UrLabelInfo *a = *(const UrLabelInfo **)aa;
6943 const UrLabelInfo *b = *(const UrLabelInfo **)bb;
6944 return
6945 a->value < b->value ? -1 :
6946 a->value > b->value ? 1 :
6951 static __attribute__((unused)) int strStartsWith (const char *s, const char *pat) {
6952 if (!pat || !pat[0]) return 0;
6953 if (!s || !s[0]) return 0;
6954 while (*s && *pat) {
6955 if (*s != *pat) return 0;
6956 ++s;
6957 ++pat;
6959 return (*pat == 0);
6963 static int isGoodLabelForRef (const UrLabelInfo *ll) {
6964 if (!ll) return 0;
6967 // do not output `=` labels
6968 if (ll->type == LBL_TYPE_ASS) return 0;
6969 // do not output struct labels
6970 if (ll->type == LBL_TYPE_STOFFS) return 0;
6973 if (urasm_dump_all_labels) {
6974 if (strStartsWith(ll->name, "UFO_") || strStartsWith(ll->name, "USE_")) return 0;
6975 return 1;
6978 for (const char *s = ll->name; *s; ++s) {
6979 const char ch = *s;
6980 if (ch >= 'A' && ch <= 'Z') continue;
6981 if (ch >= '0' && ch <= '9') continue;
6982 if (ch == '_') continue;
6983 return 1;
6986 return 0;
6990 static void writeLabelsFile (const char *fname) {
6991 if (!fname || !fname[0]) return;
6993 // count labels
6994 int lcount = 0;
6995 for (const UrLabelInfo *ll = labels; ll; ll = ll->next) {
6996 if (isGoodLabelForRef(ll)) ++lcount;
6999 UrLabelInfo **larr = NULL;
7000 if (lcount > 0) {
7001 // create labels array
7002 larr = (UrLabelInfo **)malloc(sizeof(UrLabelInfo *)*lcount);
7003 lcount = 0;
7004 for (UrLabelInfo *ll = labels; ll; ll = ll->next) {
7005 if (isGoodLabelForRef(ll)) larr[lcount++] = ll;
7007 // sort labels
7008 qsort(larr, lcount, sizeof(UrLabelInfo *), &labelCmp);
7011 // write labels
7012 FILE *fo = fopen(fname, "w");
7013 if (lcount > 0) {
7014 for (int f = 0; f < lcount; ++f) {
7015 UrLabelInfo *ll = larr[f];
7016 //if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
7017 if (!ll->name) continue; // just in case
7018 if (ll->name[0] == '{') continue; // we don't have these, but it is still reserved
7019 if (ll->value < 0) {
7020 fprintf(fo, "%d %s", ll->value, ll->name);
7021 } else {
7022 fprintf(fo, "#%04X %s", (unsigned)ll->value, ll->name);
7024 fprintf(fo, " ");
7025 switch (ll->type) {
7026 case LBL_TYPE_VERY_SPECIAL: fprintf(fo, "WTF"); break;
7027 case LBL_TYPE_UNKNOWN: fprintf(fo, "UNKNOWN"); break;
7028 case LBL_TYPE_ASS: fprintf(fo, "assign"); break;
7029 case LBL_TYPE_EQU: fprintf(fo, "equ"); break;
7030 case LBL_TYPE_CODE: fprintf(fo, "code"); break;
7031 case LBL_TYPE_STOFS: fprintf(fo, "stofs"); break;
7032 case LBL_TYPE_DATA: fprintf(fo, "data"); break;
7033 default: fprintf(fo, "WTF"); break;
7035 fputc('\n', fo);
7037 free(larr);
7040 fclose(fo);
7044 ///////////////////////////////////////////////////////////////////////////////
7045 // options
7047 static struct option longOpts[] = {
7048 {"org", required_argument, NULL, 600},
7049 {"define", required_argument, NULL, 601},
7050 {"defzero", required_argument, NULL, 602},
7051 {"outdir", required_argument, NULL, 660},
7052 {"reffile", optional_argument, NULL, 669},
7053 {"outfile", required_argument, NULL, 665},
7054 {"castrates", optional_argument, NULL, 656},
7056 {"sna", 0, NULL, 's'},
7057 {"sna128", 0, NULL, 'S'},
7058 {"tap", 0, NULL, 't'},
7059 {"autotap", 0, NULL, 'T'},
7060 {"raw", 0, NULL, 'r'},
7061 {"autodmb", 0, NULL, 'B'},
7062 {"dmb", 0, NULL, 'b'},
7063 {"none", 0, NULL, 'n'},
7064 {"help", 0, NULL, 'h'},
7065 {"hob", 0, NULL, 'H'},
7066 {"scl", 0, NULL, 'l'},
7067 {"autoscl", 0, NULL, 'L'},
7068 {"monoscl", 0, NULL, 'm'},
7069 {"autosclmono", 0, NULL, 'M'},
7070 {"dsk", 0, NULL, 690},
7071 {"dsk720", 0, NULL, 691},
7072 {"adsk", 0, NULL, 692},
7073 {"adsk720", 0, NULL, 693},
7074 {"fixups", optional_argument, NULL, 'F'},
7076 {NULL, 0, NULL, 0}
7080 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
7083 static void usage (const char *pname) {
7084 printf(
7085 "usage: %s [options] infile\n"
7086 "default infiles:", pname);
7087 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
7088 printf("\n"
7089 "options:\n"
7090 " -s --sna write 48K .SNA file with autostart\n"
7091 " -S --sna128 write 148K .SNA file with autostart\n"
7092 " -t --tap write .tap file\n"
7093 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
7094 " -r --raw write raw file(s)\n"
7095 " -b --dmb write DMB file\n"
7096 " -B --autodmb write DMB file with autostart\n"
7097 " -H --hob write HoBeta code file(s)\n"
7098 " -l --scl write SCL TR-DOS archive\n"
7099 " -L --autoscl write autostarting SCL TR-DOS archive\n"
7100 " -m --monoscl write SCL with monoloader\n"
7101 /*" -M --monosclauto write autorun SCL with monoloader\n"*/
7102 " --dsk write +3DOS 180KB disk\n"
7103 " --dsk720 write +3DOS 720KB disk\n"
7104 " --adsk write +3DOS 180KB disk with autostart\n"
7105 " --adsk720 write +3DOS 720KB disk with autostart\n"
7106 " -n --none write nothing\n"
7107 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
7108 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
7109 " text: .txt file\n"
7110 " asm: address lists with counters\n"
7111 " asmdiff: 2nd and other addresses are relative, with counters\n"
7112 " asmz: address lists, with 0 end markers\n"
7113 " -h --help this help\n"
7114 "specials:\n"
7115 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
7116 " --org xxx set ORG\n"
7117 " --define val perform 'val EQU 1'\n"
7118 " --defzero val perform 'val EQU 0'\n"
7119 " --outdir dir output dir for resulting files (default: current)\n"
7120 " --outfile fname output file name for image formats (default: main asm file name)\n"
7121 " --castrates allow '&abc' hex numbers (and disable '&b' prefix)\n"
7126 ///////////////////////////////////////////////////////////////////////////////
7127 //#include "urforth.c"
7129 enum {
7130 UFO_ZX_LABEL_UNDEFINED = -1,
7131 UFO_ZX_LABEL_UNKNOWN = 0, // referenced, but not defined yet
7132 UFO_ZX_LABEL_VAR = 1, // a = value
7133 UFO_ZX_LABEL_EQU = 2, // a equ value
7134 UFO_ZX_LABEL_CODE = 3,
7135 UFO_ZX_LABEL_DATA = 4,
7136 UFO_ZX_LABEL_STOFS = 5, // structure offset
7139 __attribute__((noreturn))
7140 void ufoFatalError (void) {
7141 longjmp(errJP, 666);
7144 static void ufoZXGetU8 (uint32_t mypfa) {
7145 uint32_t addr = ufoPopData();
7146 addr &= 0xffffU;
7147 ufoPushData(getByte(addr));
7150 static void ufoZXPutU8 (uint32_t mypfa) {
7151 uint32_t addr = ufoPopData();
7152 uint32_t v = ufoPopData();
7153 addr &= 0xffffU;
7154 v &= 0xffU;
7155 putByte(addr, v);
7158 static void ufoZXGetU16 (uint32_t mypfa) {
7159 uint32_t addr = ufoPopData();
7160 addr &= 0xffffU;
7161 ufoPushData(getWord(addr));
7164 static void ufoZXPutU16 (uint32_t mypfa) {
7165 uint32_t addr = ufoPopData();
7166 uint32_t v = ufoPopData();
7167 addr &= 0xffffU;
7168 v &= 0xffffU;
7169 putWord(addr, v);
7172 static void ufoZXEmitU8 (uint32_t mypfa) {
7173 emitByte(ufoPopData()&0xffU);
7176 static void ufoZXEmitU16 (uint32_t mypfa) {
7177 emitWord(ufoPopData()&0xffffU);
7180 static void ufoZXGetReserved (uint32_t mypfa) {
7181 uint32_t addr = ufoPopData();
7182 ufoPushData(memresv[addr&0xffffU]);
7185 static void ufoZXSetReserved (uint32_t mypfa) {
7186 uint32_t addr = ufoPopData();
7187 uint32_t resvflag = ufoPopData();
7188 memresv[addr&0xffffU] = resvflag;
7191 static void ufoZXGetPass (uint32_t mypfa) {
7192 ufoPushData(pass);
7195 static void ufoZXGetOrg (uint32_t mypfa) {
7196 ufoPushData(pc);
7199 static void ufoZXSetOrg (uint32_t mypfa) {
7200 uint32_t addr = ufoPopData();
7201 if (addr > 0xffff) ufoFatal("invalid ORG address: %u", addr);
7202 pc = disp = (uint16_t)addr;
7203 if (!wasOrg) {
7204 wasOrg = 1; // so next `ORG` will not reset it
7205 ent = (uint16_t)addr;
7209 static void ufoZXGetDisp (uint32_t mypfa) {
7210 ufoPushData(disp);
7213 static void ufoZXSetDisp (uint32_t mypfa) {
7214 uint32_t addr = ufoPopData();
7215 if (addr > 0xffff) ufoFatal("invalid DISP address: %u", addr);
7216 disp = (uint16_t)addr;
7219 static void ufoZXGetEnt (uint32_t mypfa) {
7220 ufoPushData(wasOrg ? (uint32_t)ent : ~0u);
7223 static void ufoZXSetEnt (uint32_t mypfa) {
7224 uint32_t addr = ufoPopData();
7225 if (addr > 0xffff) ufoFatal("invalid ENT address: %u", addr);
7226 wasOrg = 1; // so next `ORG` will not reset it
7227 ent = (uint16_t)addr;
7230 static int ufoZXLType2UFO (int type) {
7231 switch (type) {
7232 case LBL_TYPE_VERY_SPECIAL: return UFO_ZX_LABEL_UNDEFINED;
7233 case LBL_TYPE_UNKNOWN: return UFO_ZX_LABEL_UNKNOWN;
7234 case LBL_TYPE_ASS: return UFO_ZX_LABEL_VAR;
7235 case LBL_TYPE_EQU: return UFO_ZX_LABEL_EQU;
7236 case LBL_TYPE_CODE: return UFO_ZX_LABEL_CODE;
7237 case LBL_TYPE_DATA: return UFO_ZX_LABEL_DATA;
7238 case LBL_TYPE_STOFS: return UFO_ZX_LABEL_STOFS;
7240 ufoFatal("WTF?! unknown label type %d", type);
7243 static char ufoStrLit[256];
7245 static void ufoZXPopStrLit (void) {
7246 int32_t count = (int32_t)ufoPopData();
7247 uint32_t addr = ufoPopData();
7248 if (count < 0) ufoFatal("invalid string");
7249 uint32_t pos = 0;
7250 while (pos < (uint32_t)sizeof(ufoStrLit)) {
7251 uint8_t ch = ufoPeekByte(addr);
7252 ufoStrLit[pos] = (char)ch;
7253 if (ch == 0) break;
7254 pos += 1u; addr += 1u;
7256 if (pos == (uint32_t)sizeof(ufoStrLit)) ufoFatal("string too long");
7259 static void ufoZXHasLabel (uint32_t mypfa) {
7260 ufoZXPopStrLit();
7261 if (ufoStrLit[0] == 0) {
7262 ufoPushBoolData(0);
7263 } else {
7264 UrLabelInfo *lbl = findAddLabel(ufoStrLit);
7265 if (lbl == NULL || ufoZXLType2UFO(lbl->type) > UFO_ZX_LABEL_UNKNOWN) {
7266 ufoPushBoolData(0);
7267 } else {
7268 ufoPushBoolData(1);
7273 static void ufoZXGetLabelType (uint32_t mypfa) {
7274 ufoZXPopStrLit();
7275 if (ufoStrLit[0] == 0) {
7276 ufoPushData(UFO_ZX_LABEL_UNDEFINED);
7277 } else {
7278 UrLabelInfo *lbl = findAddLabel(ufoStrLit);
7279 if (lbl == NULL) ufoPushData(UFO_ZX_LABEL_UNDEFINED);
7280 else ufoPushData(ufoZXLType2UFO(lbl->type));
7284 static void ufoZXGetLabelValue (uint32_t mypfa) {
7285 ufoZXPopStrLit();
7286 if (ufoStrLit[0] == 0) ufoPushData(0);
7287 else {
7288 UrLabelInfo *lbl = findAddLabel(ufoStrLit);
7289 ufoPushData(lbl ? lbl->value : 0);
7293 // this also creates labels
7294 // ( value addr count -- )
7295 static void urw_set_typed_label (int type) {
7296 ufoZXPopStrLit();
7297 if (ufoStrLit[0] == 0) ufoFatal("cannot set empty label");
7298 int32_t value = (int32_t)ufoPopData();
7300 // convert label type
7301 switch (type) {
7302 case UFO_ZX_LABEL_UNDEFINED: ufoFatal("cannot set label with undefined type");
7303 case UFO_ZX_LABEL_UNKNOWN: ufoFatal("cannot set label with unknown type");
7304 case UFO_ZX_LABEL_VAR: type = LBL_TYPE_ASS; break;
7305 case UFO_ZX_LABEL_EQU: type = LBL_TYPE_EQU; break;
7306 case UFO_ZX_LABEL_CODE: type = LBL_TYPE_CODE; break;
7307 case UFO_ZX_LABEL_DATA: type = LBL_TYPE_DATA; break;
7308 case UFO_ZX_LABEL_STOFS: type = LBL_TYPE_STOFS; break;
7309 default: ufoFatal("invalid label type %d", type);
7312 UrLabelInfo *lbl = findAddLabel(ufoStrLit);
7314 if (lbl->type != LBL_TYPE_UNKNOWN && lbl->type != type) {
7315 ufoFatal("invalid label '%s' type", ufoStrLit);
7317 if (type != LBL_TYPE_ASS) {
7318 if (lbl->type >= 0 && lbl->value != value) {
7319 ufoFatal("invalid label '%s' value", ufoStrLit);
7323 lbl->value = value;
7324 if (lbl->type == LBL_TYPE_UNKNOWN) lbl->type = type;
7327 static void ufoZXSetLabelVar (uint32_t mypfa) { urw_set_typed_label(UFO_ZX_LABEL_VAR); }
7328 static void ufoZXSetLabelEqu (uint32_t mypfa) { urw_set_typed_label(UFO_ZX_LABEL_EQU); }
7329 static void ufoZXSetLabelCode (uint32_t mypfa) { urw_set_typed_label(UFO_ZX_LABEL_CODE); }
7330 static void ufoZXSetLabelData (uint32_t mypfa) { urw_set_typed_label(UFO_ZX_LABEL_DATA); }
7331 static void ufoZXSetLabelStOfs (uint32_t mypfa) { urw_set_typed_label(UFO_ZX_LABEL_STOFS); }
7333 #define UFO_ZX_MAX_ITERS (16)
7335 typedef uint32_t UFOZXLabelIterator;
7337 typedef struct {
7338 UFOZXLabelIterator id;
7339 UrLabelInfo *lbl;
7340 } UZXLabelIter;
7342 static UZXLabelIter ufoZXLblIters[UFO_ZX_MAX_ITERS];
7343 static uint32_t ufoZXLblIdLast;
7345 static int UFOZXFindIter (UFOZXLabelIterator it) {
7346 int f = 0;
7347 while (f != UFO_ZX_MAX_ITERS && ufoZXLblIters[f].id != it) f += 1;
7348 if (f == UFO_ZX_MAX_ITERS) f = -1;
7349 return f;
7352 static UrLabelInfo *UFOZXNormLabel (UrLabelInfo *c) {
7353 while (c != NULL && (c->type == LBL_TYPE_VERY_SPECIAL || c->type == LBL_TYPE_UNKNOWN)) {
7354 c = c->next;
7356 return c;
7359 static void UFOZXLabelRemoved (UrLabelInfo *lbl) {
7360 ur_assert(lbl != NULL);
7361 UrLabelInfo *c = UFOZXNormLabel(lbl->next);
7362 for (int f = 0; f < UFO_ZX_MAX_ITERS; f += 1) {
7363 if (ufoZXLblIters[f].lbl == lbl) {
7364 ufoZXLblIters[f].lbl = c;
7369 static UFOZXLabelIterator ufoZXNewLabelIter (void) {
7370 UFOZXLabelIterator res = 0;
7371 UrLabelInfo *c = UFOZXNormLabel(labels);
7372 if (c != NULL) {
7373 int f = 0;
7374 while (res == 0 && f != UFO_ZX_MAX_ITERS) {
7375 if (ufoZXLblIters[f].id == 0) {
7376 ufoZXLblIdLast += 1;
7377 if (ufoZXLblIdLast == 0) ufoZXLblIdLast = 1;
7378 ufoZXLblIters[f].id = ufoZXLblIdLast;
7379 ufoZXLblIters[f].lbl = c;
7380 res = ufoZXLblIdLast;
7384 return res;
7387 static int ufoZXLabelIterNext (UFOZXLabelIterator it) {
7388 int res = 0;
7389 int f = UFOZXFindIter(it);
7390 if (f != -1) {
7391 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7392 if (c != NULL) c = UFOZXNormLabel(c->next);
7393 if (c != NULL) {
7394 ufoZXLblIters[f].lbl = c;
7395 res = 1;
7396 } else {
7397 ufoZXLblIters[f].id = 0;
7398 ufoZXLblIters[f].lbl = NULL;
7401 return res;
7404 static void ufoZXLabelIterClose (UFOZXLabelIterator it) {
7405 int f = UFOZXFindIter(it);
7406 if (f != -1) {
7407 ufoZXLblIters[f].id = 0;
7408 ufoZXLblIters[f].lbl = NULL;
7412 static const char *ufoZXLabelIterGetName (UFOZXLabelIterator it) {
7413 int f = UFOZXFindIter(it);
7414 if (f != -1) {
7415 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7416 if (c != NULL) return c->name;
7418 return NULL;
7421 static int ufoZXIterGetValue (UFOZXLabelIterator it) {
7422 int f = UFOZXFindIter(it);
7423 if (f != -1) {
7424 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7425 if (c != NULL) return c->value;
7427 return 0;
7430 static int ufoZXIterGetType (UFOZXLabelIterator it) {
7431 int f = UFOZXFindIter(it);
7432 if (f != -1) {
7433 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7434 if (c != NULL) ufoZXLType2UFO(c->type);
7436 return UFO_ZX_LABEL_UNDEFINED;
7439 // UR-NEW-LABEL-ITER
7440 // ( -- iterid | 0 )
7441 static void ufoZXNewLabelIterWord (uint32_t mypfa) {
7442 ufoPushData(ufoZXNewLabelIter());
7445 // UR-CLOSE-LABEL-ITER
7446 // ( iterid -- )
7447 static void ufoZXCloseLabelIterWord (uint32_t mypfa) {
7448 uint32_t id = ufoPopData();
7449 ufoZXLabelIterClose(id);
7452 // UR-LABEL-ITER-NEXT
7453 // ( iterid -- not-done? )
7454 static void ufoZXNextLabelIterWord (uint32_t mypfa) {
7455 uint32_t id = ufoPopData();
7456 ufoPushBoolData(ufoZXLabelIterNext(id));
7459 // UR-LABEL-ITER-GET-NAME
7460 // ( iterid -- addr count )
7461 // to PAD
7462 static void ufoZXGetLabelNameIterWord (uint32_t mypfa) {
7463 uint32_t id = ufoPopData();
7464 const char *name = ufoZXLabelIterGetName(id);
7465 if (name == NULL) name = "";
7466 uint32_t count = 0;
7467 uint32_t pad = ufoGetPad() + 4u;
7468 while (count != 1024 && *name != 0) {
7469 ufoPokeByte(pad + count, ((const unsigned char *)name)[count]);
7470 count += 1u; name += 1u;
7472 if (count == 1024) ufoFatal("label name too long");
7473 ufoPokeByte(pad + count, 0); // just in case
7474 ufoPushData(pad); ufoPushData(count);
7477 // UR-LABEL-ITER-GET-VALUE
7478 // ( iterid -- value )
7479 static void ufoZXGetLabelValueIterWord (uint32_t mypfa) {
7480 uint32_t id = ufoPopData();
7481 ufoPushData((uint32_t)ufoZXIterGetValue(id));
7484 // UR-LABEL-ITER-GET-TYPE
7485 // ( iterid -- type )
7486 static void ufoZXGetLabelTypeIterWord (uint32_t mypfa) {
7487 uint32_t id = ufoPopData();
7488 ufoPushData((uint32_t)ufoZXIterGetType(id));
7492 static int ufoGetSrcLine (void *buf, size_t bufsize, const char **fname, int *lnum) {
7493 char *dest = (char *)buf;
7494 if (ufoIsInMacroMode()) {
7495 //fprintf(stderr, "!<%s>\n", currLine);
7496 if (strlen(currLine) >= bufsize) ufoFatal("user line too long");
7497 strcpy(buf, currLine);
7498 if (currSrcLine != NULL) {
7499 *lnum = currSrcLine->lineNo;
7500 *fname = currSrcLine->fname;
7501 } else {
7502 *lnum = -1;
7503 *fname = "wtf";
7505 } else {
7506 SourceLine *sl = nextUFOSrcLine();
7507 //fprintf(stderr, "!NL:%p:<%s>\n", sl, (sl && sl->line ? sl->line : "fuck!"));
7508 if (sl == NULL) return 0;
7509 if (sl->line == NULL) {
7510 *dest = 0;
7511 } else if (strlen(sl->line) >= bufsize) {
7512 ufoFatal("user line too long");
7513 } else {
7514 memcpy(buf, sl->line, strlen(sl->line) + 1);
7516 *lnum = sl->lineNo;
7517 *fname = sl->fname;
7519 return 1;
7522 char *ufoCreateIncludeName (const char *fname, int assystem, const char *lastIncPath) {
7523 if (lastIncPath == NULL) {
7524 if (assystem) {
7525 lastIncPath = ufoIncludeDir;
7526 } else {
7527 lastIncPath = ".";
7530 char *res = createIncludeName(fname, (assystem ? -666 : -669), NULL, lastIncPath);
7531 struct stat st;
7532 char *tmp;
7533 if (res == NULL || res[0] == 0 || stat(res, &st) != 0) return res;
7534 if (S_ISDIR(st.st_mode)) {
7535 tmp = strprintf("%s/%s", res, "00-main-loader.f");
7536 free(res);
7537 res = tmp;
7539 return res;
7540 //return createIncludeName(fname, (assystem ? -666 : -669), NULL, lastIncPath);
7544 // $END_FORTH
7545 static void ufoZXEndForth (uint32_t mypfa) {
7546 if (ufoIsInMacroMode()) ufoFatal("$END_FORTH in non-native mode");
7547 if (ufoIsCompiling()) ufoFatal("$END_FORTH: still compiling something");
7548 ufoFinishVM();
7551 static void urw_declare_typed_label (int type) {
7552 if (ufoIsCompiling()) ufoFatal("execution mode expected");
7553 ufoCallParseName();
7554 ufoZXPopStrLit();
7555 if (ufoStrLit[0] == 0) ufoFatal("label name expected");
7556 UrLabelInfo *lbl = findAddLabel(ufoStrLit);
7557 if (lbl->type != LBL_TYPE_UNKNOWN && lbl->type != type) {
7558 ufoFatal("invalid label '%s' type", ufoStrLit);
7560 if (type != LBL_TYPE_ASS) {
7561 if (lbl->type >= 0 && lbl->value != pc) {
7562 ufoFatal("invalid label '%s' value", ufoStrLit);
7566 lbl->value = pc;
7567 if (lbl->type == LBL_TYPE_UNKNOWN) lbl->type = type;
7570 // $LABEL-DATA: name
7571 static void ufoZXDefLabelData (uint32_t mypfa) { urw_declare_typed_label(LBL_TYPE_DATA); }
7572 // $LABEL-CODE: name
7573 static void ufoZXDefLabelCode (uint32_t mypfa) { urw_declare_typed_label(LBL_TYPE_CODE); }
7576 //==========================================================================
7578 // initUrForth
7580 //==========================================================================
7581 static void initUrForth (void) {
7582 for (int f = 0; f < UFO_ZX_MAX_ITERS; f += 1) {
7583 ufoZXLblIters[f].id = 0;
7584 ufoZXLblIters[f].lbl = NULL;
7586 ufoZXLblIdLast = 0;
7588 ufoMacroVocId = ufoCreateVoc("URASM-MACROS", 0, UFW_FLAG_PROTECTED);
7590 ufoVocSetOnlyDefs(ufoCreateVoc("URASM", 0, UFW_FLAG_PROTECTED));
7592 // UrAsm label types
7593 ufoRegisterConstant("LBL-TYPE-UNKNOWN", UFO_ZX_LABEL_UNKNOWN, UFW_FLAG_PROTECTED);
7594 ufoRegisterConstant("LBL-TYPE-VAR", UFO_ZX_LABEL_VAR, UFW_FLAG_PROTECTED);
7595 ufoRegisterConstant("LBL-TYPE-EQU", UFO_ZX_LABEL_EQU, UFW_FLAG_PROTECTED);
7596 ufoRegisterConstant("LBL-TYPE-CODE", UFO_ZX_LABEL_CODE, UFW_FLAG_PROTECTED);
7597 ufoRegisterConstant("LBL-TYPE-STOFS", UFO_ZX_LABEL_STOFS, UFW_FLAG_PROTECTED);
7598 ufoRegisterConstant("LBL-TYPE-DATA", UFO_ZX_LABEL_DATA, UFW_FLAG_PROTECTED);
7600 // ZX-C, ( val8 -- )
7601 // puts byte to zx dictionary
7602 ufoRegisterWord("C,", &ufoZXEmitU8, UFW_FLAG_PROTECTED);
7604 // ZX-W, ( val -- )
7605 // puts word to zx dictionary
7606 ufoRegisterWord("W,", &ufoZXEmitU16, UFW_FLAG_PROTECTED);
7608 // ZX-C@ ( addr -- value8 )
7609 ufoRegisterWord("C@", &ufoZXGetU8, UFW_FLAG_PROTECTED);
7611 // ZX-C! ( val8 addr -- )
7612 ufoRegisterWord("C!", &ufoZXPutU8, UFW_FLAG_PROTECTED);
7614 // ZX-W@ ( addr -- value16 )
7615 ufoRegisterWord("W@", &ufoZXGetU16, UFW_FLAG_PROTECTED);
7617 // ZX-W! ( val16 addr -- )
7618 ufoRegisterWord("W!", &ufoZXPutU16, UFW_FLAG_PROTECTED);
7620 // ZX-RESERVED? ( addr -- bool )
7621 ufoRegisterWord("RESERVED?", &ufoZXGetReserved, UFW_FLAG_PROTECTED);
7623 // ZX-RESERVED! ( bool addr -- )
7624 ufoRegisterWord("RESERVED!", &ufoZXSetReserved, UFW_FLAG_PROTECTED);
7626 ufoRegisterWord("HAS-LABEL?", &ufoZXHasLabel, UFW_FLAG_PROTECTED);
7627 ufoRegisterWord("LABEL-TYPE?", &ufoZXGetLabelType, UFW_FLAG_PROTECTED);
7629 ufoRegisterWord("GET-LABEL", &ufoZXGetLabelValue, UFW_FLAG_PROTECTED);
7631 ufoRegisterWord("SET-LABEL-VAR", &ufoZXSetLabelVar, UFW_FLAG_PROTECTED);
7632 ufoRegisterWord("SET-LABEL-EQU", &ufoZXSetLabelEqu, UFW_FLAG_PROTECTED);
7633 ufoRegisterWord("SET-LABEL-CODE", &ufoZXSetLabelCode, UFW_FLAG_PROTECTED);
7634 ufoRegisterWord("SET-LABEL-STOFS", &ufoZXSetLabelStOfs, UFW_FLAG_PROTECTED);
7635 ufoRegisterWord("SET-LABEL-DATA", &ufoZXSetLabelData, UFW_FLAG_PROTECTED);
7637 ufoRegisterWord("NEW-LABEL-ITER", &ufoZXNewLabelIterWord, UFW_FLAG_PROTECTED);
7638 ufoRegisterWord("CLOSE-LABEL-ITER", &ufoZXCloseLabelIterWord, UFW_FLAG_PROTECTED);
7639 ufoRegisterWord("LABEL-ITER-NEXT", &ufoZXNextLabelIterWord, UFW_FLAG_PROTECTED);
7640 ufoRegisterWord("LABEL-ITER-GET-NAME", &ufoZXGetLabelNameIterWord, UFW_FLAG_PROTECTED);
7641 ufoRegisterWord("LABEL-ITER-GET-VALUE", &ufoZXGetLabelValueIterWord, UFW_FLAG_PROTECTED);
7642 ufoRegisterWord("LABEL-ITER-GET-TYPE", &ufoZXGetLabelTypeIterWord, UFW_FLAG_PROTECTED);
7644 ufoRegisterWord("PASS@", &ufoZXGetPass, UFW_FLAG_PROTECTED);
7646 //ufoRegisterWord("LOAD-DATA-FILE", ZX_LOAD_DATA_FILE);
7648 ufoRegisterWord("ORG@", &ufoZXGetOrg, UFW_FLAG_PROTECTED);
7649 ufoRegisterWord("DISP@", &ufoZXGetDisp, UFW_FLAG_PROTECTED);
7650 ufoRegisterWord("ENT@", &ufoZXGetEnt, UFW_FLAG_PROTECTED);
7651 ufoRegisterWord("ORG!", &ufoZXSetOrg, UFW_FLAG_PROTECTED);
7652 ufoRegisterWord("DISP!", &ufoZXSetEnt, UFW_FLAG_PROTECTED);
7653 ufoRegisterWord("ENT!", &ufoZXSetDisp, UFW_FLAG_PROTECTED);
7655 ufoVocSetOnlyDefs(ufoGetForthVocId());
7657 ufoRegisterWord("$LABEL-CODE:", &ufoZXDefLabelData, UFW_FLAG_PROTECTED | UFW_FLAG_IMMEDIATE);
7658 ufoRegisterWord("$LABEL-DATA:", &ufoZXDefLabelCode, UFW_FLAG_PROTECTED | UFW_FLAG_IMMEDIATE);
7660 ufoRegisterWord("$END-FORTH", &ufoZXEndForth, UFW_FLAG_PROTECTED | UFW_FLAG_IMMEDIATE);
7661 ufoRegisterWord("$END_FORTH", &ufoZXEndForth, UFW_FLAG_PROTECTED | UFW_FLAG_IMMEDIATE);
7665 ///////////////////////////////////////////////////////////////////////////////
7666 // main
7668 int main (int argc, char *argv[]) {
7669 int res = 0, c;
7670 const char *pname = argv[0];
7671 char *inFile = NULL;
7672 char **defines = NULL, **values = NULL;
7673 int defcount = 0;
7674 int wantref = 0;
7676 ufoSetUserPostInit(&initUrForth);
7677 initLabelBuckets();
7678 modulesClear();
7679 initInclideDir();
7680 initUFEInclideDir();
7682 urasm_getbyte = getByte;
7683 urasm_putbyte = putByte;
7684 urasm_label_by_name = findLabelCB;
7685 urasm_getval = getValueCB;
7686 urasm_expand = expandCB;
7687 urasm_fixup_operand = fixupOperandCB;
7689 //strcpy(tapeLoaderName, "cargador ");
7690 tapeLoaderName[0] = 0;
7692 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
7693 while ((c = getopt_long(argc, argv, "sStTrBbnhHFLlMm", longOpts, NULL)) >= 0) {
7694 switch (c) {
7695 case '?': return 1;
7696 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
7697 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
7698 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
7699 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
7700 case 'L': optRunSCL = 1; optWriteType = 'S'; optWTChanged = 1; break; /* with boot */
7701 case 'l': optRunSCL = -1; optWriteType = 'S'; optWTChanged = 1; break; /* no boot */
7702 case 'm': optRunSCL = -1; optWriteType = 'M'; optWTChanged = 1; break; /* no boot */
7703 case 'M': optRunSCL = 1; optWriteType = 'M'; optWTChanged = 1; break; /* with boot */
7704 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
7705 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
7706 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
7707 case 'h': usage(pname); res = 0; goto earlyerrquit;
7708 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
7709 case 'F':
7710 optWriteFixups = 1;
7711 if (optarg != NULL) {
7712 if (strcmp(optarg, "asm") == 0 || strcmp(optarg, "zas") == 0) {
7713 optFixupType = 1;
7714 } else if (strcmp(optarg, "asmdiff") == 0 || strcmp(optarg, "zasdiff") == 0) {
7715 optFixupType = 2;
7716 } else if (strcmp(optarg, "asmz") == 0 || strcmp(optarg, "zasz") == 0) {
7717 optFixupType = 3;
7718 } else if (strcmp(optarg, "text") == 0) {
7719 optFixupType = 0;
7720 } else {
7721 fprintf(stderr, "FATAL: invalid fixup type: '%s'\n", optarg);
7722 return 1;
7725 break;
7726 case 600: // org
7727 c = atoi(optarg);
7728 //fprintf(stderr, "ORG: %d\n", c);
7729 if (c < 0 || c > 65535) {
7730 fprintf(stderr, "FATAL: invalid ORG: %d\n", c);
7731 return 1;
7733 start_pc = start_disp = start_ent = c;
7734 break;
7735 case 601: // define
7736 //fprintf(stderr, "define: [%s]\n", optarg);
7737 defines = realloc(defines, sizeof(char *)*(defcount+1));
7738 values = realloc(values, sizeof(char *)*(defcount+1));
7739 defines[defcount] = strdup(optarg);
7740 values[defcount] = strdup("1");
7741 ++defcount;
7742 break;
7743 case 602: // defzero
7744 //fprintf(stderr, "defzero: [%s]\n", optarg);
7745 defines = realloc(defines, sizeof(char *)*(defcount+1));
7746 values = realloc(values, sizeof(char *)*(defcount+1));
7747 defines[defcount] = strdup(optarg);
7748 values[defcount] = strdup("0");
7749 ++defcount;
7750 break;
7751 case 660: // outdir
7752 if (optOutputDir != NULL) free(optOutputDir);
7753 optOutputDir = strdup(optarg);
7754 break;
7755 case 665: // outdir
7756 if (optOutputFile != NULL) free(optOutputFile);
7757 optOutputFile = strdup(optarg);
7758 break;
7759 case 669: // reffile
7760 if (refFileName) free(refFileName);
7761 refFileName = (optarg ? strdup(optarg) : NULL);
7762 wantref = 1;
7763 break;
7764 case 690: case 692: // +3DOS 180K disk
7765 optRunDSK = (c == 692);
7766 optDskType = P3DSK_PCW_SS;
7767 optWriteType = '3';
7768 optWTChanged = 1;
7769 break;
7770 case 691: case 693: // +3DOS 720K disk
7771 optRunDSK = (c == 693);
7772 optDskType = P3DSK_PCW_DS;
7773 optWriteType = '3';
7774 optWTChanged = 1;
7775 break;
7776 case 656: // hex castrates
7777 if (optarg != NULL) {
7778 if (strcmp(optarg, "tan") == 0 || strcmp(optarg, "yes") == 0) {
7779 urasm_allow_hex_castrates = 1;
7780 } else if (strcmp(optarg, "ona") == 0 || strcmp(optarg, "no") == 0) {
7781 urasm_allow_hex_castrates = 0;
7782 } else {
7783 fprintf(stderr, "FATAL: i don't know what '%s' means.\n", optarg);
7784 return 1;
7786 } else {
7787 urasm_allow_hex_castrates = 1;
7789 break;
7793 if (optind >= argc) {
7794 // try to find default input file
7795 for (int f = 0; defInFiles[f]; ++f) {
7796 if (!access(defInFiles[f], R_OK)) {
7797 inFile = strdup(defInFiles[f]);
7798 break;
7801 } else {
7802 inFile = strdup(argv[optind]);
7805 if (!inFile || !inFile[0]) {
7806 res = 1;
7807 fprintf(stderr, "ERROR: no input file!\n");
7808 goto earlyerrquit;
7811 if (optOutputDir == NULL) optOutputDir = strdup(".");
7812 #ifdef WIN32
7813 for (char *ss = optOutputDir; *ss; ++ss) if (*ss == '\\') *ss = '/';
7814 #endif
7816 char *ee = strrchr(optOutputDir, '/');
7817 if (ee && ee != optOutputDir) *ee = 0;
7819 if (!optOutputDir[0]) { free(optOutputDir); optOutputDir = strdup("."); }
7821 registerInstructions();
7822 registerFunctions();
7824 res = asmTextLoad(inFile, 0);
7825 if (res == 0) {
7826 // init UrForth here, because why not?
7827 ufoInit();
7828 ufoFileReadLine = &ufoGetSrcLine;
7830 for (int f = 0; f < defcount; ++f) {
7831 if (labelDoEQU(defines[f], values[f]) != 0) {
7832 fprintf(stderr, "FATAL: can't define label: '%s'\n", defines[f]);
7833 goto errquit;
7835 //fprintf(stderr, "****SET**** <%s> to <%s>\n", defines[f], values[f]);
7838 for (pass = 0; pass <= 1; ++pass) {
7839 initPass();
7840 printf("pass %d\n", pass);
7841 //fprintf(stderr, "===========pass %d===========\n", pass);
7842 setCurSrcLine(asmText);
7843 if (setjmp(errJP)) { res = 1; break; }
7844 while (currSrcLine) processCurrentLine();
7845 if (postPass()) { res = 1; break; }
7848 // write result
7849 if (res == 0) {
7850 char *oc = strdup(inFile);
7851 char *pd = strrchr(oc, '.');
7852 #ifdef WIN32
7853 if (pd && !strchr(oc, '/') && !strchr(oc, '\\')) *pd = '\0';
7854 #else
7855 if (pd && !strchr(oc, '/')) *pd = '\0';
7856 #endif
7857 switch (optWriteType) {
7858 case 's': saveSna(oc, optSNA48); break;
7859 case 't': saveTap(oc); break;
7860 case 'r': saveRaw(oc); break;
7861 case 'd': saveDMB(oc); break;
7862 case 'H': saveHob(oc); break;
7863 case 'S': saveSCL(oc); break;
7864 case 'M': saveSCLMono(oc); break;
7865 case '3': saveDSK(oc); break;
7867 free(oc);
7868 if (optWriteFixups) writeFixups();
7869 if (wantref) {
7870 /* build ref file name */
7871 if (!refFileName || !refFileName[0]) {
7872 if (refFileName) free(refFileName);
7873 refFileName = malloc(strlen(inFile)+128);
7874 strcpy(refFileName, inFile);
7875 char *ext = strrchr(refFileName, '.');
7876 if (!ext) {
7877 strcat(refFileName, ".ref");
7878 } else {
7879 #ifdef WIN32
7880 char *slash = NULL;
7881 for (char *ts = refFileName; *ts; ++ts) {
7882 if (*ts == '/' || *ts == '\\') slash = ts;
7884 #else
7885 char *slash = strrchr(refFileName, '/');
7886 #endif
7887 if (!slash || slash < ext) {
7888 strcpy(ext, ".ref");
7889 } else {
7890 strcat(refFileName, ".ref");
7894 writeLabelsFile(refFileName);
7895 printf("refs written to '%s'\n", refFileName);
7896 free(refFileName);
7899 } else {
7900 fprintf(stderr, "ERROR: loading error!\n");
7903 errquit:
7904 ufoDeinit();
7905 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
7906 clearFixups();
7907 urClearLabels();
7908 modulesClear();
7909 asmTextClear();
7910 urClearOps();
7911 clearMacros();
7912 freeStructs();
7913 clearKnownFiles();
7915 earlyerrquit:
7916 if (inFile) free(inFile);
7917 if (sysIncludeDir) free(sysIncludeDir);
7918 if (ufoIncludeDir) free(ufoIncludeDir);
7919 if (lastIncludePath) free(lastIncludePath);
7920 if (lastSysIncludePath) free(lastSysIncludePath);
7921 for (int f = defcount-1; f >= 0; --f) {
7922 free(values[f]);
7923 free(defines[f]);
7925 if (defines != NULL) { free(values); free(defines); }
7926 if (optOutputFile) free(optOutputFile);
7927 if (optOutputDir) free(optOutputDir);
7929 return (res ? 1 : 0);