UrForth: switched interceptors to scattered colons
[urasm.git] / src / urasm.c
blobfc9b90f626aab844a98e98af647042535529b8ec
1 // URASM Z80 assembler
2 // coded by Ketmar // Invisible Vector
3 // GPLv3 ONLY
4 //
5 #ifndef _GNU_SOURCE
6 # define _GNU_SOURCE
7 #endif
9 #include <getopt.h>
10 #include <setjmp.h>
11 #include <stdarg.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
23 #ifdef WIN32
24 # include <windows.h>
25 #endif
27 #include "liburasm/liburasm.h"
28 #include "libfdc/libfdc.h"
29 #include "urforth.h"
31 #include "ursna48.c"
34 #define UR_FORCE_INLINE static inline __attribute__((always_inline))
35 #define UR_INLINE static inline
38 #define VERSION_HI 0
39 #define VERSION_MID 2
40 #define VERSION_LO 2
43 #define MAYBE_UNUSED __attribute__((unused))
45 #define lambda(return_type, body_and_args) ({ \
46 return_type __fn__ body_and_args \
47 __fn__; \
51 // ////////////////////////////////////////////////////////////////////////// //
52 static const char *ur_assert_failure (const char *cond, const char *fname, int fline,
53 const char *func)
55 for (const char *t = fname; *t; ++t) {
56 #ifdef WIN32
57 if (*t == '/' || *t == '\\') fname = t+1;
58 #else
59 if (*t == '/') fname = t+1;
60 #endif
62 fflush(stdout);
63 fprintf(stderr, "\n%s:%d: Assertion in `%s` failed: %s\n", fname, fline, func, cond);
64 fflush(stderr);
65 abort();
68 #define ur_assert(cond_) do { if (__builtin_expect((!(cond_)), 0)) { ur_assert_failure(#cond_, __FILE__, __LINE__, __PRETTY_FUNCTION__); } } while (0)
71 //==========================================================================
73 // joaatHashStr
75 //==========================================================================
76 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];
214 ///////////////////////////////////////////////////////////////////////////////
215 // init dirs
217 static void initInclideDir (void) {
218 const char *id = getenv("URASM_INCLUDE_DIR");
219 if (id && id[0]) {
220 sysIncludeDir = strdup(id);
221 } else {
222 char myDir[4096];
223 memset(myDir, 0, sizeof(myDir));
224 #ifndef WIN32
225 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
226 strcpy(myDir, ".");
227 } else {
228 char *p = (char *)strrchr(myDir, '/');
229 if (!p) strcpy(myDir, "."); else *p = '\0';
231 #else
232 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
233 char *p = strrchr(myDir, '\\');
234 if (!p) strcpy(myDir, "."); else *p = '\0';
235 #endif
236 strcat(myDir, "/libs");
237 sysIncludeDir = strdup(myDir);
239 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
240 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
244 static void initUFEInclideDir (void) {
245 const char *id = getenv("URASM_URFORTH_INCLUDE_DIR");
246 if (id && id[0]) {
247 ufoIncludeDir = strdup(id);
248 } else {
249 char myDir[4096];
250 memset(myDir, 0, sizeof(myDir));
251 #ifndef WIN32
252 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
253 strcpy(myDir, ".");
254 } else {
255 char *p = (char *)strrchr(myDir, '/');
256 if (!p) strcpy(myDir, "."); else *p = '\0';
258 #else
259 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
260 char *p = strrchr(myDir, '\\');
261 if (!p) strcpy(myDir, "."); else *p = '\0';
262 #endif
263 strcat(myDir, "/urflibs");
264 ufoIncludeDir = strdup(myDir);
266 while (ufoIncludeDir[0] && ufoIncludeDir[strlen(ufoIncludeDir)-1] == '/') ufoIncludeDir[strlen(ufoIncludeDir)-1] = '\0';
267 if (!ufoIncludeDir[0]) strcpy(ufoIncludeDir, ".");
271 ///////////////////////////////////////////////////////////////////////////////
272 // string utilities
274 /* trim trailing spaces and comments; normalize colons */
275 static void normalizeStr (char *s) {
276 if (asmMode == AMODE_UFO) return; // do nothing
277 char *p = s;
278 /* now skip all shit */
279 while (*p) {
280 const char ch = *p++;
281 /* check for "af'" */
282 if (ch == '\'' && p-s >= 2 && toLower(p[-2] == 'a') && toLower(p[-1] == 'f')) continue;
283 /* comment */
284 if (ch == ';') { p[-1] = 0; break; }
285 /* string */
286 if (ch == '"' || ch == '\'') {
287 const char qch = ch;
288 while (*p) {
289 const char c1 = *p++;
290 if (c1 == qch) break;
291 if (c1 == '\\' && *p) ++p;
293 continue;
295 /* reduce and normalise colons */
296 if (ch == ':') {
297 --p; /* back to colon */
298 /* remove spaces before colon */
299 char *t = p;
300 while (t != s && isSpace(t[-1])) --t;
301 if (t != p) memmove(t, p, strlen(p)+1);
302 p = t;
303 ur_assert(p[0] == ':');
304 ++p; /* skip colon */
305 /* remove following spaces and colons */
306 t = p;
307 while (*t == ':' || isSpace(*t)) ++t;
308 if (t != p) memmove(p, t, strlen(t)+1);
309 continue;
312 /* done; trim trailing spaces and colons */
313 size_t slen = strlen(s);
314 while (slen > 0 && (isSpace(s[slen-1]) || s[slen-1] == ':')) --slen;
315 s[slen] = 0;
319 /* check if string starts with the given command (case-insensitive) */
320 /* returns NULL or pointer to args */
321 /* skips spaces after command if any */
322 static char *strIsCommand (const char *command, char *str) {
323 for (int cnt = 1; cnt > 0; --cnt) {
324 while (*str && isSpace(*str)) ++str; // skip spaces
325 if (*str == '$') ++str;
326 for (; *command && *str; ++command, ++str) {
327 if (toUpper(*command) != toUpper(*str)) return NULL; // alas
329 if (*command) return NULL; // alas
330 if (*str && isAlphaDigit(*str)) return NULL; // alas
331 while (*str && isSpace(*str)) ++str; // skip spaces
332 if (asmMode != AMODE_UFO) {
333 if (*str && *str == ':') break; // try again if we have a colon
335 return str; // found
337 return NULL;
341 /* parse string literal */
342 /* don't free() result */
343 /* skips trailing spaces */
344 static char *parseStr (char **str, char endQ, int *lenp) {
345 static char buf[MAX_LINE_SIZE];
346 int len = 0, n, f, base;
347 char *a = *str;
348 memset(buf, 0, sizeof(buf));
349 if (lenp) *lenp = 0;
350 for (; *a; ++a) {
351 if (*a == '\\') {
352 if (!a[1]) break;
353 switch (*(++a)) {
354 case 'a': buf[len++] = '\a'; break;
355 case 'b': buf[len++] = '\b'; break;
356 case 'e': buf[len++] = '\x1b'; break;
357 case 'f': buf[len++] = '\f'; break;
358 case 'n': buf[len++] = '\n'; break;
359 case 'r': buf[len++] = '\r'; break;
360 case 't': buf[len++] = '\t'; break;
361 case 'v': buf[len++] = '\v'; break;
362 case 'z': buf[len++] = '\0'; break;
363 case 'x': case 'X': // hex
364 ++a; // skip 'x'
365 base = 16; f = 2;
366 donum: for (n = 0; f > 0; --f) {
367 char ch = digitInBase(*a++, base);
369 if (ch < 0) { --a; break; }
370 n *= base;
371 n += ch;
373 buf[len++] = n;
374 --a; // return to the last digit, 'for' will skip it
375 break;
376 case '0': // octal
377 base = 8; f = 4;
378 goto donum;
379 case '1' ... '9': // decimal
380 base = 10; f = 3;
381 goto donum;
382 default: buf[len++] = a[0]; break; // others
384 } else {
385 if (*a == endQ) { ++a; break; }
386 buf[len++] = *a;
389 while (*a && isSpace(*a)) ++a; // skip trailing spaces
390 *str = a;
391 buf[len] = '\0';
392 if (lenp) *lenp = len;
393 return buf;
397 ///////////////////////////////////////////////////////////////////////////////
398 // source file stack, reader, etc
400 typedef struct SourceLine {
401 struct SourceLine *next;
402 char *line;
403 const char *fname;
404 int lineNo;
405 int system;
406 } SourceLine;
408 static SourceLine *asmText = NULL;
409 static SourceLine *currSrcLine = NULL;
411 typedef struct FileInfo_s {
412 struct FileInfo_s *next;
413 char *name;
414 } FileInfo;
416 static FileInfo *knownFiles = NULL;
419 static void clearKnownFiles (void) {
420 while (knownFiles) {
421 FileInfo *fi = knownFiles;
422 knownFiles = fi->next;
423 if (fi->name) free(fi->name);
424 free(fi);
429 static const char *appendKnownFileName (const char *fn) {
430 if (!fn || !fn[0]) return "<unknown>";
431 for (FileInfo *fi = knownFiles; fi; fi = fi->next) {
432 if (strcmp(fi->name, fn) == 0) return fi->name;
434 FileInfo *fi = calloc(1, sizeof(FileInfo));
435 ur_assert((fi->name = strdup(fn)) != NULL);
436 fi->next = knownFiles;
437 knownFiles = fi;
438 return fi->name;
442 // ////////////////////////////////////////////////////////////////////////// //
443 #define MAX_MACRO_ARGS (32)
445 typedef struct MacroDef {
446 struct MacroDef *next;
447 char *name;
448 SourceLine *lines;
449 int argc;
450 char *argdefaults[MAX_MACRO_ARGS]; // default values
451 char *argnames[MAX_MACRO_ARGS]; // argument names
452 } MacroDef;
454 typedef struct {
455 MacroDef *mac;
456 char *argvals[MAX_MACRO_ARGS]; // argument values
457 } CurMacroDef;
459 static MacroDef *maclist = NULL;
460 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
461 static int curmacronum = 0;
464 static void freeLineList (SourceLine *list) {
465 while (list) {
466 SourceLine *l = list;
467 list = l->next;
468 free(l->line);
469 //free(l->fname);
470 free(l);
475 static void clearMacros (void) {
476 while (maclist) {
477 MacroDef *md = maclist;
478 maclist = md->next;
479 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) {
480 if (md->argdefaults[f]) free(md->argdefaults[f]);
481 if (md->argnames[f]) free(md->argnames[f]);
483 free(md->name);
484 freeLineList(md->lines);
485 free(md);
490 static MacroDef *findMacro (const char *name) {
491 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strEquCI(name, mc->name)) return mc;
492 return NULL;
496 static void asmTextClear (void) {
497 freeLineList(asmText);
498 asmText = NULL;
499 currSrcLine = NULL;
503 #define currLineTrimBlanks() do { \
504 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
505 currLine[slen] = 0; \
506 } while (0)
508 #define currLineRemoveComment() do { \
509 size_t pos = 0; \
510 char instr = 0; \
511 while (pos < slen) { \
512 if (instr) { \
513 if (currLine[pos] == '\\' && pos+1 < slen) { \
514 ++pos; \
515 } else if (currLine[pos] == instr) { \
516 instr = 0; \
518 } else { \
519 if (currLine[pos] == ';') { slen = pos; break; } \
520 if (currLine[pos] == '"') { \
521 instr = '"'; \
522 } else if (currLine[pos] == '\'') { \
523 if (pos == 0 || \
524 (currLine[pos-1] != '_' && !isAlphaDigit(currLine[pos-1]))) \
526 instr = currLine[pos]; \
530 ++pos; \
532 while (slen && *(uint8_t *)(currLine+slen-1) <= 32) --slen; \
533 currLine[slen] = 0; \
534 } while (0)
537 static SourceLine *loadTextFile (FILE *fl, int system, const char *fname, int *error) {
538 if (error) *error = 0;
540 fname = appendKnownFileName(fname);
541 int continuation = 0;
543 SourceLine *first = NULL;
544 SourceLine *last = NULL;
545 int lineNo = 0;
547 // read file
548 while (fgets(currLine, sizeof(currLine)-1, fl)) {
549 ++lineNo;
550 currLine[sizeof(currLine)-1] = '\0';
551 size_t slen = strlen(currLine);
553 if (slen == 0 || (currLine[slen-1] != '\n' && currLine[slen-1] != '\r')) {
554 if (!feof(fl)) {
555 if (error) *error = 1;
556 fprintf(stderr, "ERROR: file %s, line %d: line too long\n", fname, lineNo);
557 freeLineList(first);
558 return NULL;
562 currLineTrimBlanks();
564 int new_cont = 0;
565 if (slen && currLine[slen-1] == '\\') {
566 // this is a possible continuation line
567 if (slen > 1 && currLine[slen-2] == '\\') {
568 // nope, fix it
569 slen -= 1;
570 currLine[slen] = 0;
571 } else {
572 new_cont = 1;
573 --slen;
574 currLineTrimBlanks();
578 //!normalizeStr(currLine);
579 if (!currLine[0]) {
580 // don't store empty lines
581 continuation = new_cont;
582 continue;
585 // remove comments from continuations
586 if (continuation || new_cont) {
587 currLineRemoveComment();
590 // continuation?
591 if (continuation) {
592 // join continuation with the last line
593 ur_assert(last);
594 size_t newlen = slen+strlen(last->line)+4;
595 if (newlen > MAX_LINE_SIZE-8) {
596 if (error) *error = 1;
597 fprintf(stderr, "ERROR: too long continuation at line %d in file '%s'\n", lineNo, fname);
598 freeLineList(first);
599 return NULL;
601 char *newbuf = malloc(newlen);
602 ur_assert(newbuf); // who cares
603 snprintf(newbuf, newlen, "%s%s", last->line, currLine);
604 free(last->line);
605 last->line = newbuf;
606 } else {
607 // add current line
608 SourceLine *s = calloc(1, sizeof(SourceLine));
609 ur_assert(s);
610 s->system = system;
611 s->lineNo = lineNo;
612 ur_assert((s->line = strdup(currLine)) != NULL);
613 s->fname = fname;
614 if (last) last->next = s; else first = s;
615 last = s;
618 continuation = new_cont;
621 return first;
625 static int asmTextLoad (const char *fname, int system) {
626 FILE *fl;
627 int error;
629 if (!(fl = fopen(fname, "r"))) {
630 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
631 return -1;
633 printf("loading: %s\n", fname);
635 SourceLine *first = loadTextFile(fl, 0, fname, &error);
636 fclose(fl);
638 if (error) return -1;
640 SourceLine *last = asmText;
641 if (last) {
642 while (last->next) last = last->next;
643 last->next = first;
644 } else {
645 asmText = first;
647 return 0;
651 static char *extractFileDir (const char *s) {
652 if (!s || !s[0]) return strdup(".");
653 const char *slash;
654 #ifdef WIN32
655 slash = NULL;
656 for (const char *ts = s; *ts; ++ts) {
657 if (*ts == '/' || *ts == '\\') slash = ts;
659 if (slash == s && (s[0] == '/' || s[0] == '\\') && (s[1] == '/' || s[1] == '\\')) slash = NULL;
660 #else
661 slash = strrchr(s, '/');
662 #endif
663 if (!slash) return strdup(".");
664 ptrdiff_t len = (ptrdiff_t)(slash-s)+1;
665 char *res = malloc(len+1);
666 memcpy(res, s, len);
667 res[len] = 0;
668 #ifdef WIN32
669 while (len > 0 && (res[len-1] == '\\' || res[len-1] == '/')) --len;
670 #else
671 while (len > 0 && res[len-1] == '/') --len;
672 #endif
673 if (len == 0) { free(res); return strdup("."); }
674 res[len] = 0;
675 return res;
679 static void loadCurSrcLine (void) {
680 if (currSrcLine) {
681 strcpy(currLine, currSrcLine->line);
682 /* macros will not change include dirs */
683 if (!curmacro) {
684 char **incpp = (!currSrcLine->system ? &lastIncludePath : &lastSysIncludePath);
685 if (*incpp) free(*incpp);
686 *incpp = extractFileDir(currSrcLine->fname);
688 normalizeStr(currLine);
689 } else {
690 currLine[0] = 0;
695 UR_FORCE_INLINE SourceLine *setCurSrcLine (SourceLine *l) {
696 currSrcLine = l;
697 loadCurSrcLine();
698 return l;
702 UR_FORCE_INLINE SourceLine *setUFOCurSrcLine (SourceLine *l) {
703 currSrcLine = l;
704 if (currSrcLine) {
705 strcpy(currLine, currSrcLine->line);
706 } else {
707 currLine[0] = 0;
709 return l;
713 UR_FORCE_INLINE SourceLine *nextSrcLine (void) {
714 return (currSrcLine != NULL ? setCurSrcLine(currSrcLine->next) : NULL);
718 UR_FORCE_INLINE SourceLine *nextUFOSrcLine (void) {
719 return (currSrcLine != NULL ? setUFOCurSrcLine(currSrcLine->next) : NULL);
723 UR_FORCE_INLINE int strHasPathDelim (const char *s) {
724 if (!s || !s[0]) return 0;
725 #ifdef WIN32
726 return (strchr(s, '/') || strchr(s, '\\') ? 1 : 0);
727 #else
728 return (strchr(s, '/') ? 1 : 0);
729 #endif
733 /* returns malloced string */
734 static char *createIncludeName (const char *fname, int assystem, const char *defaultmain,
735 const char *lastIncPathUFO) {
736 if (!fname || !fname[0]) return NULL;
737 char *res;
738 if (fname[0] != '/') {
739 const char *incdir;
740 if (!assystem) {
741 incdir = lastIncludePath;
742 } else if (assystem == -669) {
743 incdir = lastIncPathUFO;
744 } else if (assystem == -666) {
745 incdir = lastIncPathUFO;
746 if (!incdir || !incdir[0]) incdir = ufoIncludeDir;
747 } else {
748 incdir = lastSysIncludePath;
749 if (!incdir || !incdir[0]) incdir = sysIncludeDir;
751 if (incdir == NULL || incdir[0] == 0) incdir = ".";
752 res = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
753 } else {
754 res = strprintf("%s", fname);
756 struct stat st;
757 if (defaultmain && defaultmain[0]) {
758 if (stat(res, &st) == 0) {
759 if (S_ISDIR(st.st_mode)) {
760 char *rs = strprintf("%s/%s", res, defaultmain);
761 free(res);
762 res = rs;
766 /* check if there is the disk file */
767 if (strHasPathDelim(fname) && stat(res, &st) != 0) {
768 /* no file, try "root include" */
769 const char *incdir = (!assystem ? NULL : assystem == -666 ? ufoIncludeDir : sysIncludeDir);
770 char *rs = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
771 free(res);
772 res = rs;
773 /* check for dir again */
774 if (defaultmain && defaultmain[0]) {
775 if (stat(res, &st) == 0) {
776 if (S_ISDIR(st.st_mode)) {
777 char *rs = strprintf("%s/%s", res, defaultmain);
778 free(res);
779 res = rs;
784 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
785 return res;
789 static int includeCount = 0;
791 // process 'INCLUDE'
792 // include file instead of the current line
793 // returns 0 on ok, <0 on error
794 static int asmTextInclude (const char *fname, int system, int softinclude) {
795 char *fn = createIncludeName(fname, system, "zzmain.zas", NULL);
797 FILE *fl = fopen(fn, "r");
798 if (!fl) {
799 if (softinclude) return 0;
800 fprintf(stderr, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", currSrcLine->fname, currSrcLine->lineNo, currLine);
801 free(fn);
802 return -1;
805 if (includeCount > 256) {
806 fclose(fl);
807 free(fn);
808 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", currSrcLine->fname, currSrcLine->lineNo);
809 return -1;
812 ++includeCount;
813 printf("loading: %s\n", fn);
815 int error;
816 SourceLine *first = loadTextFile(fl, system, fn, &error);
817 fclose(fl);
818 free(fn);
820 if (error) return -1;
822 --includeCount;
823 currSrcLine->line[0] = 0; // remove `include` directive
825 SourceLine *last = first;
826 if (last) {
827 while (last->next) last = last->next;
828 last->next = currSrcLine->next;
829 currSrcLine->next = first;
831 return 0;
835 ///////////////////////////////////////////////////////////////////////////////
836 // prototypes
838 static void processCurrentLine (void); // only one, will skip to next one
841 ///////////////////////////////////////////////////////////////////////////////
842 // error raisers, etc
844 static jmp_buf errJP;
847 static void errorWriteFile (FILE *fo) {
848 if (currSrcLine) {
849 size_t slen = strlen(currSrcLine->line);
850 fprintf(fo, "at file %s, line %d\n%.70s%s\n*", currSrcLine->fname,
851 currSrcLine->lineNo, currSrcLine->line, (slen > 70 ? " ..." : ""));
852 } else {
853 fprintf(fo, "somewhere in time: ");
858 static void errorMsgV (const char *fmt, va_list ap) {
859 errorWriteFile(stderr);
860 vfprintf(stderr, fmt, ap);
861 va_end(ap);
862 fputc('\n', stderr);
863 fflush(stderr);
867 __attribute__((format(printf, 1, 2)))
868 static void warningMsg (const char *fmt, ...) {
869 va_list ap;
870 fprintf(stderr, "WARNING ");
871 va_start(ap, fmt);
872 errorMsgV(fmt, ap);
876 __attribute__((format(printf, 1, 2)))
877 static void errorMsg (const char *fmt, ...) {
878 va_list ap;
879 fprintf(stderr, "FATAL ");
880 va_start(ap, fmt);
881 errorMsgV(fmt, ap);
885 __attribute__((noreturn)) __attribute__((format(printf, 1, 2)))
886 static void fatal (const char *fmt, ...) {
887 va_list ap;
888 va_start(ap, fmt);
889 errorMsgV(fmt, ap);
890 longjmp(errJP, 666);
894 __attribute__((noreturn))
895 static void fatalUrLib (int errcode) {
896 errorMsg("%s", urasm_errormsg(errcode));
897 longjmp(errJP, 666);
901 ///////////////////////////////////////////////////////////////////////////////
902 // writer options
903 static int optWriteType = 't';
904 static int optWTChanged = 0;
905 static int optWriteFixups = 0;
906 static int optFixupType = 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
907 static int optRunTape = 1;
908 static int optRunDMB = 1;
909 static int optRunSCL = -1; /* -1: save cargador, but no boot; 1: boot and cargador */
910 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
911 static int optSNA48 = 1;
912 static char *optOutputDir = NULL;
913 static char *optOutputFile = NULL;
914 static int optOutputFileFixed = 0;
917 static char *buildOutputFileName (const char *basename, const char *ext) {
918 ur_assert(ext);
919 ur_assert(basename);
920 if (ext[0] == '.') ++ext;
921 //fprintf(stderr, "********** <%s> <%s> <%s> %d <%s>\n", basename, ext, optOutputFile, optOutputFileFixed, optOutputDir);
922 if (optOutputFile) {
923 if (!optOutputFileFixed) {
924 optOutputFileFixed = 1;
925 char *spp = strrchr(optOutputFile, '/');
926 #ifdef WIN32
927 char *spp1 = strrchr(optOutputFile, '\\');
928 if (!spp || (spp < spp1)) spp = spp1;
929 #endif
930 if (!spp) spp = optOutputFile; else ++spp;
931 char *dotpos = strrchr(spp, '.');
932 if (dotpos) *dotpos = 0;
934 if (optOutputFile && optOutputFile[0]) return strprintf("%s/%s.%s", optOutputDir, optOutputFile, ext);
936 return strprintf("%s/%s.%s", optOutputDir, basename, ext);
940 typedef struct OutDataFile_t {
941 char *name;
942 char type; // 'C', 'B'
943 int hasstart;
944 uint16_t start; // start address, or basic start line
945 uint16_t size; // data size (never 0)
946 uint8_t *data;
947 struct OutDataFile_t *next;
948 } OutDataFile;
950 static OutDataFile *datafiles = NULL;
953 static OutDataFile *appendDataData (const void *bytes, uint16_t start, uint16_t count) {
954 if (!count) return NULL;
956 OutDataFile *off = malloc(sizeof(OutDataFile));
957 off->name = NULL;
958 off->type = 'C';
959 off->hasstart = 0;
960 off->start = start;
961 off->size = count;
962 off->data = malloc(count);
963 if (bytes) memcpy(off->data, bytes, count); else memset(off->data, 0, count);
964 off->next = NULL;
966 OutDataFile *last = datafiles;
967 if (!last) {
968 datafiles = off;
969 } else {
970 while (last->next) last = last->next;
971 last->next = off;
974 return off;
978 static OutDataFile *appendDataFile (const char *fname, long ofs, int len, int allowskip) {
979 if (!fname || !fname[0]) return NULL;
981 FILE *fl = fopen(fname, "rb");
982 if (!fl) {
983 if (allowskip) return NULL;
984 fatal("cannot open data file '%s'", fname);
986 if (fseek(fl, 0, SEEK_END) < 0) fatal("error reading data file '%s'", fname);
987 long fsize = ftell(fl);
988 if (fsize < 0) fatal("error reading data file '%s'", fname);
989 if (ofs < 0) {
990 // from file end
991 ofs = -ofs;
992 if (ofs < 0) fatal("invalid offset");
993 if (ofs >= fsize) ofs = 0; else ofs = fsize-ofs;
995 if (ofs >= fsize) { fclose(fl); return NULL; }
996 if (fseek(fl, ofs, SEEK_SET) < 0) fatal("error reading data file '%s'", fname);
997 fsize -= ofs;
998 if (len < 0) {
999 if (fsize > 65535) fatal("data file '%s' too big", fname);
1000 len = (int)fsize;
1002 if (len > 65535) fatal("data file '%s' too big", fname);
1003 if (len == 0) { fclose(fl); return NULL; }
1005 OutDataFile *off = malloc(sizeof(OutDataFile));
1006 off->name = NULL;
1007 off->type = 'C';
1008 off->hasstart = 0;
1009 off->start = 0;
1010 off->size = (uint16_t)len;
1011 off->data = malloc(len);
1012 off->next = NULL;
1013 if (fread(off->data, (unsigned)len, 1, fl) != 1) fatal("error reading data file '%s'", fname);
1014 fclose(fl);
1016 OutDataFile *last = datafiles;
1017 if (!last) {
1018 datafiles = off;
1019 } else {
1020 while (last->next) last = last->next;
1021 last->next = off;
1024 return off;
1028 //////////////////////////////////////////////////////////////////////////////
1029 // operator management
1031 // return !0 to skip current line
1032 typedef int (*UrAsmOpFn) (void);
1034 enum {
1035 PI_CONT_LINE = 0,
1036 PI_SKIP_LINE = 1
1039 typedef struct UrAsmOp_t {
1040 uint32_t hash;
1041 struct UrAsmOp_t *hlink;
1042 // other
1043 char *name;
1044 UrAsmOpFn fn;
1045 void *udata; // user data (struct info for structs, for example)
1046 struct UrAsmOp_t *next;
1047 } UrAsmOp;
1049 #define UR_OPER_BUCKETS (256)
1050 static UrAsmOp *oplist = NULL;
1051 static UrAsmOp *urCurrentOp = NULL;
1052 static UrAsmOp *operBuckets[UR_OPER_BUCKETS];
1055 //==========================================================================
1057 // urAddOpEx
1059 //==========================================================================
1060 static UrAsmOp *urAddOpEx (const char *name, UrAsmOpFn fn, char optpfx) {
1061 ur_assert(name != NULL && name[0] != 0);
1062 if (oplist == NULL) {
1063 //fprintf(stderr, "CLEAR OPBUCKETS!\n");
1064 for (int f = 0; f < UR_OPER_BUCKETS; f += 1) operBuckets[f] = NULL;
1066 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
1067 ur_assert(res);
1068 if (optpfx != 0) {
1069 res->name = calloc(1, strlen(name) + 2);
1070 ur_assert(res->name);
1071 res->name[0] = optpfx;
1072 strcpy(res->name + 1, name);
1073 } else {
1074 res->name = strdup(name);
1075 ur_assert(res->name);
1077 res->fn = fn;
1078 res->udata = NULL;
1079 res->next = oplist;
1080 oplist = res;
1081 // insert into hash table
1082 res->hash = joaatHashStrCI(res->name);
1083 const uint32_t bkt = res->hash%UR_OPER_BUCKETS;
1084 //fprintf(stderr, "NEWOP: 0x%08x (%u) : <%s>\n", res->hash, bkt, res->name);
1085 res->hlink = operBuckets[bkt];
1086 operBuckets[bkt] = res;
1087 return res;
1091 //==========================================================================
1093 // urAddOp
1095 //==========================================================================
1096 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
1097 return urAddOpEx(name, fn, 0);
1101 //==========================================================================
1103 // urAddOpAndDollar
1105 //==========================================================================
1106 static void urAddOpAndDollar (const char *name, UrAsmOpFn fn) {
1107 (void)urAddOpEx(name, fn, 0);
1108 (void)urAddOpEx(name, fn, '$');
1112 //==========================================================================
1114 // urFindOp
1116 //==========================================================================
1117 static UrAsmOp *urFindOp (const char *name) {
1118 if (name == NULL || name[0] == 0) return NULL;
1119 #if 0
1120 UrAsmOp *res;
1121 if (name[0] == '$' && name[1]) {
1122 for (res = oplist; res; res = res->next) {
1123 if (res->name[0] == '$') {
1124 if (strEquCI(name, res->name)) break;
1125 } else {
1126 if (strEquCI(name+1, res->name)) break;
1129 } else {
1130 for (res = oplist; res; res = res->next) if (strEquCI(name, res->name)) break;
1132 return res;
1133 #else
1134 const uint32_t hash = joaatHashStrCI(name);
1135 //fprintf(stderr, "LOOK: 0x%08x (%u) : <%s>\n", hash, hash%UR_OPER_BUCKETS, name);
1136 UrAsmOp *res = operBuckets[hash%UR_OPER_BUCKETS];
1137 while (res != NULL && (res->hash != hash || !strEquCI(name, res->name))) {
1138 //fprintf(stderr, " REJECT: 0x%08x: <%s>\n", res->hash, res->name);
1139 res = res->hlink;
1141 return res;
1142 #endif
1146 //==========================================================================
1148 // urClearOps
1150 //==========================================================================
1151 static void urClearOps (void) {
1152 while (oplist != NULL) {
1153 UrAsmOp *c = oplist;
1154 oplist = oplist->next;
1155 free(c->name);
1156 free(c);
1158 for (int f = 0; f < UR_OPER_BUCKETS; f += 1) operBuckets[f] = NULL;
1162 ///////////////////////////////////////////////////////////////////////////////
1163 // label management
1165 enum {
1166 LBL_TYPE_VERY_SPECIAL = -42,
1167 LBL_TYPE_UNKNOWN = -1,
1168 LBL_TYPE_ASS = 0, // `=`
1169 LBL_TYPE_EQU = 1, // equ
1170 LBL_TYPE_CODE = 2, // ':', or "lbl opcode"
1171 LBL_TYPE_STOFS = 3, // struct offset
1172 LBL_TYPE_DATA = 4,
1175 typedef struct UrLabelInfo_t {
1176 uint32_t hash;
1177 struct UrLabelInfo_t *hlink;
1178 // other
1179 char *name;
1180 int32_t value;
1181 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
1182 int known; /* !0: label value already known */
1183 int refLine; /* first referenced line */
1184 int fixuptype; /* UR_FIXUP_XXX */
1185 int modHidden; /* hidden module label? hack for PLUS3BOOT */
1186 char savedFirstChar; /* for hidden labels */
1187 char *refFile;
1188 struct UrLabelInfo_t *next;
1189 // previous temp label
1190 struct UrLabelInfo_t *prevTemp;
1191 int tempIdx;
1192 int tempFwd;
1193 } UrLabelInfo;
1196 #define UR_LABEL_BUCKETS (256)
1197 static UrLabelInfo *labels = NULL;
1198 static UrLabelInfo *labelsTail = NULL;
1199 static UrLabelInfo *labelBuckets[UR_LABEL_BUCKETS];
1200 // list of all @@, from the last defined
1201 static UrLabelInfo *tempLabels = NULL;
1202 static int tempLabelIdx = 0;
1205 //==========================================================================
1207 // initLabelBuckets
1209 //==========================================================================
1210 static void initLabelBuckets (void) {
1211 for (int f = 0; f < UR_LABEL_BUCKETS; f += 1) labelBuckets[f] = NULL;
1215 //==========================================================================
1217 // urClearLabels
1219 //==========================================================================
1220 static void urClearLabels (void) {
1221 UrLabelInfo *c;
1222 while ((c = labels) != NULL) {
1223 labels = c->next;
1224 if (c->name) free(c->name);
1225 if (c->refFile) free(c->refFile);
1226 free(c);
1228 labelsTail = NULL;
1229 initLabelBuckets();
1233 //==========================================================================
1235 // urFindLabel
1237 //==========================================================================
1238 static UrLabelInfo *urFindLabel (const char *name) {
1239 if (name != NULL && name[0] != 0) {
1240 #if 0
1241 for (UrLabelInfo *c = labels; c; c = c->next) {
1242 if (strcmp(name, c->name) == 0) return c;
1244 #else
1245 const uint32_t hash = joaatHashStr(name);
1246 UrLabelInfo *c = labelBuckets[hash%UR_LABEL_BUCKETS];
1247 while (c != NULL) {
1248 if (c->hash == hash && strcmp(c->name, name) == 0) return c;
1249 c = c->hlink;
1251 #endif
1253 return NULL;
1257 //==========================================================================
1259 // urAddLabel
1261 //==========================================================================
1262 static UrLabelInfo *urAddLabel (const char *name) {
1263 ur_assert(name != NULL && name[0] != 0);
1264 UrLabelInfo *c = urFindLabel(name);
1265 if (c == NULL) {
1266 c = calloc(1, sizeof(UrLabelInfo));
1267 ur_assert(c);
1268 c->name = strdup(name);
1269 ur_assert(c->name);
1270 c->type = LBL_TYPE_UNKNOWN;
1271 c->fixuptype = UR_FIXUP_NONE;
1272 c->modHidden = 0;
1273 c->savedFirstChar = 0;
1274 c->next = NULL;
1275 if (labelsTail) labelsTail->next = c; else labels = c;
1276 labelsTail = c;
1277 c->hash = joaatHashStr(c->name);
1278 const uint32_t bkt = c->hash%UR_LABEL_BUCKETS;
1279 c->hlink = labelBuckets[bkt];
1280 labelBuckets[bkt] = c;
1282 return c;
1286 //==========================================================================
1288 // urAddTempLabel
1290 //==========================================================================
1291 static UrLabelInfo *urAddTempLabel (void) {
1292 char name[32];
1293 tempLabelIdx += 1;
1294 snprintf(name, sizeof(name), "@@_%05d", tempLabelIdx);
1295 UrLabelInfo *lbl = urAddLabel(name);
1296 lbl->tempIdx = tempLabelIdx;
1297 lbl->prevTemp = tempLabels;
1298 tempLabels = lbl;
1299 return lbl;
1303 //==========================================================================
1305 // urNextTempLabel
1307 //==========================================================================
1308 static UrLabelInfo *urNextTempLabel (void) {
1309 char name[32];
1310 tempLabelIdx += 1;
1311 snprintf(name, sizeof(name), "@@_%05d", tempLabelIdx);
1312 UrLabelInfo *lbl = urFindLabel(name);
1313 ur_assert(lbl->tempIdx == tempLabelIdx);
1314 ur_assert(lbl->prevTemp == NULL);
1315 lbl->prevTemp = tempLabels;
1316 tempLabels = lbl;
1317 return lbl;
1321 //==========================================================================
1323 // urResetTempLabels
1325 //==========================================================================
1326 static void urResetTempLabels (void) {
1327 while (tempLabels != NULL) {
1328 UrLabelInfo *lbl = tempLabels;
1329 tempLabels = lbl->prevTemp;
1330 lbl->prevTemp = NULL;
1332 tempLabels = NULL;
1333 tempLabelIdx = 0;
1337 //==========================================================================
1339 // urAddTempLocal
1341 // add local label; it may shadow others
1343 //==========================================================================
1344 static UrLabelInfo *urAddTempLocal (const char *name) {
1345 UrLabelInfo *c = calloc(1, sizeof(UrLabelInfo));
1346 ur_assert(c);
1347 c->name = strdup(name);
1348 ur_assert(c->name);
1349 c->type = LBL_TYPE_EQU;
1350 c->known = 1;
1351 c->fixuptype = UR_FIXUP_NONE;
1352 c->modHidden = 0;
1353 c->savedFirstChar = 0;
1354 c->next = labels;
1355 labels = c;
1356 if (!labelsTail) labelsTail = c;
1357 c->hash = joaatHashStr(c->name);
1358 const uint32_t bkt = c->hash%UR_LABEL_BUCKETS;
1359 c->hlink = labelBuckets[bkt];
1360 labelBuckets[bkt] = c;
1361 return c;
1365 static void UFOZXLabelRemoved (UrLabelInfo *lbl);
1368 //==========================================================================
1370 // urRemoveTempLocal
1372 //==========================================================================
1373 static void urRemoveTempLocal (UrLabelInfo *tmplbl) {
1374 if (!tmplbl) return;
1375 ur_assert(tmplbl == labels);
1376 UFOZXLabelRemoved(tmplbl);
1377 labels = tmplbl->next;
1378 if (!labels) labelsTail = NULL;
1379 const uint32_t bkt = tmplbl->hash%UR_LABEL_BUCKETS;
1380 ur_assert(labelBuckets[bkt] == tmplbl);
1381 labelBuckets[bkt] = tmplbl->hlink;
1382 free(tmplbl->name);
1383 free(tmplbl);
1387 ///////////////////////////////////////////////////////////////////////////////
1388 // structures
1389 typedef struct StructField_s {
1390 struct StructField_s *next; // next field
1391 char *name; // field name
1392 uint16_t ofs; // field offset in struct
1393 uint16_t size; // field size in bytes (can be 0 for aliases)
1394 int32_t initValue;
1395 int hasInit;
1396 } StructField;
1399 typedef struct StructInfo_s {
1400 struct StructInfo_s *next; // next structure
1401 char *name; // structure name
1402 StructField *fields;
1403 uint16_t size; // total structure size in bytes
1404 int zerofill;
1405 } StructInfo;
1408 // list of all structures
1409 static StructInfo *structList = NULL;
1412 static void freeStructs (void) {
1413 while (structList != NULL) {
1414 StructInfo *sth = structList;
1415 structList = sth->next;
1416 StructField *fld = sth->fields;
1417 while (fld) {
1418 StructField *fc = fld;
1419 fld = fld->next;
1420 if (fc->name) free(fc->name);
1421 free(fc);
1423 if (sth->name) free(sth->name);
1424 free(sth);
1429 ///////////////////////////////////////////////////////////////////////////////
1430 // fixup management
1431 typedef struct FixupItem_s {
1432 struct FixupItem_s *next;
1433 uint16_t opdestaddr;
1434 uint16_t opaddr;
1435 int fixuptype;
1436 int size;
1437 } FixupItem;
1438 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
1441 static void clearFixups (void) {
1442 FixupItem *c;
1443 while ((c = fixlisthead) != NULL) {
1444 fixlisthead = c->next;
1445 free(c);
1447 fixlisthead = fixlisttail = NULL;
1451 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
1452 FixupItem *fx = calloc(1, sizeof(FixupItem));
1454 fx->opdestaddr = opdestaddr;
1455 fx->opaddr = opaddr;
1456 fx->fixuptype = fixuptype;
1457 fx->size = size;
1459 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
1460 fx->next = NULL;
1461 fixlisttail = fx;
1465 ///////////////////////////////////////////////////////////////////////////////
1466 // destination memory management
1468 static uint8_t memory[65536];
1469 static uint8_t memused[65536];
1470 static uint8_t memresv[65536];
1471 static uint16_t start_pc = 0x100; // viva CP/M!
1472 static uint16_t start_disp = 0x100; // viva CP/M!
1473 static uint16_t start_ent = 0x100; // viva CP/M!
1474 static uint16_t pc = 0; /* current position to write */
1475 static uint16_t disp = 0; /* current 'virtual PC' */
1476 static uint16_t ent = 0; /* starting address */
1477 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
1478 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
1479 static int inTapeBlock = 0;
1480 static uint8_t tapeXorB = 0;
1482 static int findChunkFrom (int addr, int *start, int *len);
1485 // find next free area starting from `addr`
1486 // returns address or -1
1487 static int reserveFindFreeFrom (uint16_t addr) {
1488 for (uint32_t freeaddr = addr; freeaddr <= 0xffffU; ++freeaddr) {
1489 if (!memresv[freeaddr]) return freeaddr;
1491 return -1;
1495 // find next reserved area starting from `addr`
1496 // returns address or -1
1497 static int reserveFindReservedFrom (uint16_t addr) {
1498 for (uint32_t freeaddr = addr; freeaddr <= 0xffffU; ++freeaddr) {
1499 if (memresv[freeaddr]) return freeaddr;
1501 return -1;
1505 static void reserveReleaseUsed (uint16_t addr, int len) {
1506 while (len--) memused[addr++] = 0;
1510 //TODO: wrapping
1511 static int reserveHit (uint16_t addr, int len) {
1512 if (len <= 0) return 0;
1513 while (len--) {
1514 if (memresv[addr++]) return 1;
1516 return 0;
1520 // start from `addr`, look for free areas, and check
1521 // if we can insert jump from a pcaddr there
1522 // returns next free area or -1
1523 static int reservedFindNextJumpableFreeFrom (uint16_t pcaddr, uint16_t addr) {
1524 // check if there is enough room at least for a jr
1525 if (memresv[pcaddr] || memresv[pcaddr+1]) return -1;
1526 uint16_t caddr = addr;
1527 // skip reserved
1528 while (caddr <= 0xffffU && memresv[caddr]) ++caddr;
1529 if (caddr > 0xffffU) return -1; // alas
1530 // check distance
1531 uint32_t jrdist = caddr-(pcaddr+2U);
1532 if (jrdist < 128u) return (int)caddr; // yay, we can JR there, this free area is good
1533 // cannot JR, check if we have room for JP
1534 if (memresv[pcaddr+2]) return -1; // alas
1535 // ok, can JP
1536 return (int)caddr;
1540 // start from `addr`, look for free areas, and check
1541 // if we can insert jump from a pcaddr there, and if
1542 // that area is big enough to hold a next jump and at least one more byte
1543 // returns next free area or -1
1544 static int reservedFindNextSuitableFreeFrom (uint16_t pcaddr, uint16_t addr) {
1545 int caddr = addr;
1546 for (;;) {
1547 caddr = reservedFindNextJumpableFreeFrom(pcaddr, (uint16_t)caddr);
1548 if (caddr < 0) return -1; // alas
1549 // we can jump to naddr, now check if it is big enough
1550 // if it has less than 3 bytes, it is too small
1551 if (memresv[(uint16_t)(caddr+1)] || memresv[(uint16_t)(caddr+2)]) {
1552 // too small
1553 caddr += 3;
1554 continue;
1556 // if we have more than 3 bytes, it is definitely ok
1557 if (!memresv[(uint16_t)(caddr+3)]) return caddr; // ok
1558 // it has exactly 3 bytes, check if it is enough
1559 int xaddr = reservedFindNextJumpableFreeFrom(caddr+1, (uint16_t)caddr+3);
1560 if (xaddr >= 0) return caddr; // ok
1561 // alas
1562 caddr = reserveFindReservedFrom(caddr);
1563 if (caddr < 0) return -1;
1564 caddr = reserveFindFreeFrom(caddr);
1565 if (caddr < 0) return -1;
1569 UR_FORCE_INLINE uint8_t getByte (uint16_t addr) {
1570 return memory[addr];
1574 UR_FORCE_INLINE uint16_t getWord (uint16_t addr) {
1575 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
1579 UR_FORCE_INLINE void putByte (uint16_t addr, uint8_t b) {
1580 if (inTapeBlock) tapeXorB ^= b;
1581 memory[addr] = b;
1582 memused[addr] = 1;
1586 UR_FORCE_INLINE void putWord (uint16_t addr, uint16_t w) {
1587 putByte(addr, w&0xFFU);
1588 putByte(addr+1, (w>>8)&0xFFU);
1592 UR_FORCE_INLINE void emitByte (uint8_t b) {
1593 putByte(pc, b);
1594 ++pc;
1595 ++disp;
1599 UR_FORCE_INLINE void emitWord (uint16_t w) {
1600 emitByte(w&0xFFU);
1601 emitByte((w>>8)&0xFFU);
1605 UR_FORCE_INLINE void emitRWord (uint16_t w) {
1606 emitByte((w>>8)&0xFFU);
1607 emitByte(w&0xFFU);
1611 static void prepareMemory (void) {
1612 memset(memory, 0, sizeof(memory));
1613 memset(memused, 0, sizeof(memused));
1614 memset(memresv, 0, sizeof(memresv));
1618 ///////////////////////////////////////////////////////////////////////////////
1619 // module list management
1621 typedef struct ModuleInfo_t {
1622 char *name;
1623 char *fname; // opened in this file
1624 int seen; // !0: module already seen, skip other definitions from the same file
1625 struct ModuleInfo_t *next;
1626 uint32_t hash;
1627 struct ModuleInfo_t *hlink;
1628 } ModuleInfo;
1630 #define UR_MODULE_BUCKETS (1024)
1631 static ModuleInfo *modules = NULL;
1632 static ModuleInfo *currModule = NULL;
1633 static ModuleInfo *moduleBuckets[UR_MODULE_BUCKETS];
1635 static uint8_t mod_memory_saved[65536];
1636 static uint8_t mod_memused_saved[65536];
1637 static uint8_t mod_memresv_saved[65536];
1640 static void modulesClear (void) {
1641 currModule = NULL;
1642 while (modules) {
1643 ModuleInfo *c = modules;
1644 modules = modules->next;
1645 free(c->name);
1646 free(c->fname);
1647 free(c);
1649 for (int f = 0; f < UR_MODULE_BUCKETS; f +=1) moduleBuckets[f] = NULL;
1653 //==========================================================================
1655 // modulesResetSeen
1657 //==========================================================================
1658 static void modulesResetSeen (void) {
1659 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
1663 //==========================================================================
1665 // moduleFind
1667 //==========================================================================
1668 static ModuleInfo *moduleFind (const char *name) {
1669 if (!name || !name[0]) return NULL;
1670 #if 0
1671 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
1672 #else
1673 const uint32_t hash = joaatHashStr(name);
1674 ModuleInfo *c = moduleBuckets[hash%UR_MODULE_BUCKETS];
1675 while (c != NULL) {
1676 if (c->hash == hash && strcmp(c->name, name) == 0) return c;
1677 c = c->hlink;
1679 #endif
1680 return NULL;
1684 //==========================================================================
1686 // moduleAdd
1688 // special module name: PLUS3BOOT
1690 //==========================================================================
1691 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
1692 ModuleInfo *c;
1693 ur_assert(name && fname && name[0] && fname[0]);
1694 c = calloc(1, sizeof(ModuleInfo));
1695 ur_assert(c);
1696 c->name = strdup(name);
1697 ur_assert(c->name);
1698 c->fname = strdup(fname);
1699 ur_assert(c->fname);
1700 c->next = modules;
1701 modules = c;
1702 c->hash = joaatHashStr(name);
1703 const uint32_t bkt = c->hash%UR_MODULE_BUCKETS;
1704 c->hlink = moduleBuckets[bkt];
1705 moduleBuckets[bkt] = c;
1706 // +3DOS bootsector?
1707 if (strEquCI(c->name, "PLUS3BOOT")) {
1708 // save current memory state, because bootsector will be written as a separate independent file
1709 memcpy(mod_memory_saved, memory, sizeof(mod_memory_saved));
1710 memcpy(mod_memused_saved, memused, sizeof(mod_memused_saved));
1711 memcpy(mod_memresv_saved, memresv, sizeof(mod_memresv_saved));
1713 return c;
1717 //==========================================================================
1719 // moduleOpen
1721 //==========================================================================
1722 static void moduleOpen (void) {
1723 //fprintf(stderr, "::: OPENING MODULE <%s> :::\n", currModule->name);
1724 // unhide +3DOS boot labels
1725 if (strEquCI(currModule->name, "PLUS3BOOT")) {
1726 for (UrLabelInfo *lbl = labels; lbl; lbl = lbl->next) {
1727 if (lbl->modHidden == 1) {
1728 if (!lbl->savedFirstChar) fatal("internal module manager error");
1729 lbl->modHidden = 0;
1730 lbl->name[0] = lbl->savedFirstChar; // unhide it
1731 //fprintf(stderr, "***UN-HIDDEN: <%s>\n", lbl->name);
1738 //==========================================================================
1740 // isModuleLabel
1742 //==========================================================================
1743 static int isModuleLabel (const ModuleInfo *mi, const char *name) {
1744 if (!mi || !name || !name[0]) return 0;
1745 if (name[0] == '.') ++name;
1746 const size_t mnlen = strlen(mi->name);
1747 if (strlen(name) <= mnlen || name[mnlen] != '.') return 0;
1748 return (memcmp(name, mi->name, mnlen) == 0);
1752 //==========================================================================
1754 // moduleClose
1756 // close current module
1758 //==========================================================================
1759 static void moduleClose (const char *mname) {
1760 if (mname && !mname[0]) mname = NULL;
1761 if (!currModule) {
1762 if (!mname) fatal("trying to close unopened module");
1763 fatal("trying to close unopened module '%s'", mname);
1765 if (mname && strcmp(mname, currModule->name)) fatal("invalid module name in ENDMODULE; got '%s', expected '%s'", mname, currModule->name);
1767 // +3DOS bootsector?
1768 if (strEquCI(currModule->name, "PLUS3BOOT")) {
1769 // append bootsector chunk
1770 int start = 0, len;
1772 if (!findChunkFrom(start, &start, &len)) fatal("no +3DOS bootsector chunk");
1773 if (start != 0xFE10) fatal("+3DOS bootsector chunk must be 'org'ed and #FE10");
1774 if (len > 512-16) fatal("+3DOS bootsector chunk is too big");
1776 if (pass == 1) {
1777 OutDataFile *off = appendDataData(memory+(unsigned)start, (unsigned)start, (unsigned)len);
1778 if (!off) fatal("cannot append +3DOS bootsector");
1779 off->name = strdup("");
1780 off->type = 1; // special type
1782 start += len;
1783 if (findChunkFrom(start, &start, &len)) fatal("only +3DOS bootsector chunk is allowed (found #%04X, %d)", (unsigned)start, len);
1786 // restore memory
1787 memcpy(memory, mod_memory_saved, sizeof(memory));
1788 memcpy(memused, mod_memused_saved, sizeof(memused));
1789 memcpy(memresv, mod_memresv_saved, sizeof(memresv));
1791 // remove module labels
1792 for (UrLabelInfo *lbl = labels; lbl; lbl = lbl->next) {
1793 if (!isModuleLabel(currModule, lbl->name)) continue;
1794 //fprintf(stderr, "***HIDDEN: <%s>\n", lbl->name);
1795 lbl->modHidden = 1;
1796 lbl->savedFirstChar = lbl->name[0];
1797 lbl->name[0] = '{'; // hide it
1801 currModule = NULL;
1805 ///////////////////////////////////////////////////////////////////////////////
1806 // label getter and utilities
1808 static char *lastSeenGlobalLabel = NULL; /* global; malloced */
1811 static char *mangleLabelName (const char *name) {
1812 static char newname[MAX_LINE_SIZE*2+1024];
1813 if (!name || !name[0]) {
1814 newname[0] = '\0';
1815 return newname;
1817 // local label or macro label?
1818 if (name[0] == '.') {
1819 // two dots is a macro label
1820 if (name[1] == '.') {
1821 if (!name[2]) fatal("invalid label '%s'", name);
1822 // this is macro label
1823 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
1824 snprintf(newname, sizeof(newname), ".%s.MACLBL%d%s", curmacro->mac->name, curmacronum, name+1);
1825 return newname;
1827 if (!name[1]) fatal("invalid label '%s'", name);
1828 // simple local
1829 if (!lastSeenGlobalLabel) fatal("local label '%s' without a global", name);
1830 snprintf(newname, sizeof(newname), ".%s%s", lastSeenGlobalLabel, name);
1831 return newname;
1833 // global label
1834 // if global is prefixed with '@', it should not be mangled
1835 // this is to allow access to other labels from modules
1836 if (name[0] == '@') {
1837 if (name[1] == '@') fatal("double-prefixed label '%s'", name);
1838 if (!name[1]) fatal("invalid label '%s'", name);
1839 snprintf(newname, sizeof(newname), "%s", name+1);
1840 return newname;
1842 // if no module, nothing to do
1843 // also, do not mangle labels with dots inside:
1844 // those are either already mangled, or call to other modules
1845 if (!currModule || strchr(name, '.')) {
1846 snprintf(newname, sizeof(newname), "%s", name);
1847 return newname;
1849 // this is global unqualified label and we have a module; let's rename it
1850 snprintf(newname, sizeof(newname), "%s.%s", currModule->name, name);
1851 return newname;
1855 static UrLabelInfo *addTempLabel (void) {
1856 UrLabelInfo *lbl = tempLabels;
1857 if (lbl == NULL || !lbl->tempFwd) {
1858 if (pass == 0) {
1859 lbl = urAddTempLabel();
1860 } else {
1861 lbl = urNextTempLabel();
1863 lbl->tempFwd = 1;
1864 if (!lbl->refFile) {
1865 lbl->refLine = currSrcLine->lineNo;
1866 lbl->refFile = strdup(currSrcLine->fname);
1869 return lbl;
1873 static UrLabelInfo *addFwdTempLabel (void) {
1874 return addTempLabel();
1878 // WARNING! this will be invalidated
1879 static char lastFindLabelName[MAX_LINE_SIZE*2+1024];
1882 static UrLabelInfo *findLabel (const char *name) {
1883 UrLabelInfo *lbl;
1884 if (!name || !name[0]) fatal("UrAsm internal error: empty name in `findLabel()`");
1885 // temp label?
1886 if (name[0] == '@') {
1887 if ((name[1] == '@' && name[2] == 'b' && name[3] == 0) ||
1888 (name[1] == 'b' && name[2] == 0))
1890 // last defined temp
1891 lbl = tempLabels;
1892 while (lbl != NULL && lbl->tempFwd) lbl = lbl->prevTemp;
1893 if (lbl == NULL) fatal("no temp labels defined yet");
1894 return lbl;
1896 if ((name[1] == '@' && name[2] == 'f' && name[3] == 0) ||
1897 (name[1] == 'f' && name[2] == 0))
1899 // next defined temp
1900 return addFwdTempLabel();
1903 // try temp label
1904 if (name[0] == '@' && name[1] == '@' && name[2] == '_') {
1905 //fprintf(stderr, "TEMP: <%s>\n", name);
1906 lbl = urFindLabel(name);
1907 if (lbl) return lbl;
1909 char *nn = mangleLabelName(name);
1910 strcpy(lastFindLabelName, nn);
1911 lbl = urFindLabel(nn);
1913 if (!lbl) {
1914 // try non-module label
1915 lbl = urFindLabel(ln);
1918 return lbl;
1922 static UrLabelInfo *findAddLabel (const char *name) {
1923 if (!name || !name[0]) fatal("UrAsm internal error: empty name in `findAddLabel()`");
1924 char *nn = mangleLabelName(name);
1925 strcpy(lastFindLabelName, nn);
1926 UrLabelInfo *lbl = urAddLabel(nn);
1927 if (!lbl->refFile) {
1928 lbl->refLine = currSrcLine->lineNo;
1929 lbl->refFile = strdup(currSrcLine->fname);
1931 return lbl;
1935 static int lblOptMakeU2 = 0;
1937 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
1938 // predefined
1939 if (strEquCI(name, "__URASM_DEFFMT_ANY_DISK")) {
1940 *found = 1;
1941 *defined = 1;
1942 *fixuptype = UR_FIXUP_NONE;
1943 return (optWriteType == 'S' || optWriteType == '3'); // SCL or +3DOS
1945 if (strEquCI(name, "__URASM_DEFFMT_SCL")) {
1946 *found = 1;
1947 *defined = 1;
1948 *fixuptype = UR_FIXUP_NONE;
1949 return (optWriteType == 'S');
1951 if (strEquCI(name, "__URASM_DEFFMT_SCL_MONO")) {
1952 *found = 1;
1953 *defined = 1;
1954 *fixuptype = UR_FIXUP_NONE;
1955 return (optWriteType == 'M');
1957 if (strEquCI(name, "__URASM_DEFFMT_P3DOS")) {
1958 *found = 1;
1959 *defined = 1;
1960 *fixuptype = UR_FIXUP_NONE;
1961 return (optWriteType == '3');
1963 if (strEquCI(name, "__URASM_DEFFMT_HOBETA")) {
1964 *found = 1;
1965 *defined = 1;
1966 *fixuptype = UR_FIXUP_NONE;
1967 return (optWriteType == 'H');
1969 if (strEquCI(name, "__URASM_DEFFMT_TAPE")) {
1970 *found = 1;
1971 *defined = 1;
1972 *fixuptype = UR_FIXUP_NONE;
1973 return (optWriteType == 't');
1975 if (strEquCI(name, "__URASM_DEFFMT_ANY_SNA")) {
1976 *found = 1;
1977 *defined = 1;
1978 *fixuptype = UR_FIXUP_NONE;
1979 return (optWriteType == 's');
1981 if (strEquCI(name, "__URASM_DEFFMT_SNA48")) {
1982 *found = 1;
1983 *defined = 1;
1984 *fixuptype = UR_FIXUP_NONE;
1985 return (optWriteType == 's' && optSNA48);
1987 if (strEquCI(name, "__URASM_DEFFMT_SNA128")) {
1988 *found = 1;
1989 *defined = 1;
1990 *fixuptype = UR_FIXUP_NONE;
1991 return (optWriteType == 's' && !optSNA48);
1993 if (strEquCI(name, "__URASM_DEFFMT_RAW")) {
1994 *found = 1;
1995 *defined = 1;
1996 *fixuptype = UR_FIXUP_NONE;
1997 return (optWriteType == 'r');
1999 if (strEquCI(name, "__URASM_DEFFMT_DMB")) {
2000 *found = 1;
2001 *defined = 1;
2002 *fixuptype = UR_FIXUP_NONE;
2003 return (optWriteType == 'd');
2006 UrLabelInfo *lbl = findLabel(name);
2007 if (!lbl) {
2008 if (pass != 0) {
2009 //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);
2010 errorMsg("using undefined label '%s'", name);
2011 *found = 0;
2012 *defined = 0;
2013 return 0;
2015 lbl = urAddLabel(lastFindLabelName);
2016 lbl->type = (lblOptMakeU2 ? LBL_TYPE_VERY_SPECIAL : LBL_TYPE_UNKNOWN);
2017 lbl->known = 0;
2018 lbl->refLine = currSrcLine->lineNo;
2019 lbl->refFile = strdup(currSrcLine->fname);
2020 //fprintf(stderr, "new label: [%s]\n", lbl->name);
2021 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2022 } else {
2023 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
2025 if (lbl) {
2026 *found = 1;
2027 *defined = (lbl->known != 0);
2028 *fixuptype = lbl->fixuptype;
2029 return lbl->value;
2031 *found = 0;
2032 *defined = 0;
2033 return 0;
2037 // qtypes
2038 enum {
2039 UR_QTYPE_DEFINED,
2040 UR_QTYPE_KNOWN
2043 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
2044 UrLabelInfo *lbl = findLabel(name);
2045 switch (qtype) {
2046 case UR_QTYPE_DEFINED: return (lbl ? lbl->known != 0 : 0);
2047 case UR_QTYPE_KNOWN: return (lbl != NULL);
2048 default: ;
2050 return 0;
2054 static void fixupOperandCB (const urasm_operand_t *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
2055 if (pass == 1) {
2056 //static const char *n[4] = {"none", "word", "low", "high"};
2057 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
2058 addFixup(opdestaddr, opaddr, fixuptype, size);
2063 static int checkLabels (void) {
2064 int wasError = 0;
2065 for (UrLabelInfo *c = labels; c; c = c->next) {
2066 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
2067 if (c->type == LBL_TYPE_UNKNOWN) {
2068 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
2069 wasError = 1;
2071 if (c->type == LBL_TYPE_ASS) c->known = -1;
2073 //if (wasError) longjmp(errJP, 667);
2074 return wasError;
2078 ///////////////////////////////////////////////////////////////////////////////
2079 // expression utils (aka string parsing)
2082 /* skip leading spaces */
2083 /* returns string with spaces skipped */
2084 UR_FORCE_INLINE char *strSkipSpaces (const char *s) {
2085 while (*s && isSpace(*s)) ++s;
2086 return (char *)s;
2090 /* skip leading spaces */
2091 /* returns string with spaces skipped */
2092 UR_FORCE_INLINE const char *strSkipSpacesConst (const char *s) {
2093 while (*s && isSpace(*s)) ++s;
2094 return s;
2098 /* remove trailing spaces from string */
2099 static void strTrimRight (char *s) {
2100 if (!s || !s[0]) return;
2101 size_t len = strlen(s);
2102 while (len > 0 && isSpace(s[len-1])) --len;
2103 s[len] = 0;
2107 /* skip leading spaces and colons */
2108 /* returns string with spaces skipped */
2109 UR_FORCE_INLINE char *strSkipSpacesColons (char *s) {
2110 while (*s && (isSpace(*s) || *s == ':')) ++s;
2111 return s;
2115 /* remove leading spaces from the current line */
2116 UR_FORCE_INLINE void removeSpaces (void) {
2117 char *e = strSkipSpaces(currLine);
2118 if (e != currLine) memmove(currLine, e, strlen(e)+1);
2122 /* skip leading spaces, and argument (up to, but not including comma or colon) */
2123 /* correctly skip strings */
2124 /* returns string after skipped argument (with trailing spaces skipped) */
2125 static char *skipMacroArg (char *str) {
2126 int parens = 0;
2127 char *strstart = str;
2128 for (;;) {
2129 str = strSkipSpaces(str);
2130 if (!str[0]) return str;
2131 if (parens == 0 && (str[0] == ',' || str[0] == ':')) return str;
2132 /* check for "af'" */
2133 if (str[0] == '\'' && str-strstart >= 2 && toLower(str[-2] == 'a') && toLower(str[-1] == 'f')) {
2134 ++str;
2135 continue;
2137 if (str[0] == '(') { ++parens; str += 1; continue; }
2138 if (str[0] == ')') { --parens; if (parens < 0) parens = 0; str += 1; continue; }
2139 /* check for string */
2140 if (str[0] == '"' || str[0] == '\'') {
2141 const char qch = *str++;
2142 while (*str) {
2143 const char ch = *str++;
2144 if (ch == qch) break;
2145 if (ch == '\\' && *str) ++str;
2147 continue;
2149 ++str;
2154 /* evaluate next numeric expression in input string */
2155 /* returns expression value */
2156 static int32_t getExprArg (int *defined, int *addr) {
2157 int error = 0;
2158 char *a = strSkipSpaces(currLine);
2159 int32_t res;
2160 if (!a[0]) fatal("expression expected");
2161 const char *ee = urasm_expr(&res, a, disp, defined, addr, &error);
2162 if (error) fatalUrLib(error);
2163 if (*ee) {
2164 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
2165 memmove(currLine, ee, strlen(ee)+1);
2166 } else {
2167 currLine[0] = '\0';
2169 return res;
2173 /* evaluate next string expression in input string */
2174 /* returns expression value */
2175 static char *getStrExprArgFmt (void) {
2176 int error = 0;
2177 int donteval = 0, defined = 0;
2178 static char resbuf[256];
2179 char *a = strSkipSpaces(currLine);
2180 if (!a[0]) fatal("expression expected");
2181 urasm_exprval_t res;
2182 urasm_exprval_init(&res);
2183 const char *ee = urasm_expr_ex(&res, a, disp, &donteval, &defined, &error);
2184 if (error) fatalUrLib(error);
2185 if (*ee) {
2186 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
2187 memmove(currLine, ee, strlen(ee)+1);
2188 } else {
2189 currLine[0] = '\0';
2191 if (res.str) {
2192 snprintf(resbuf, sizeof(resbuf), "%s", res.str);
2193 } else {
2194 snprintf(resbuf, sizeof(resbuf), "%d", res.val);
2196 urasm_exprval_clear(&res);
2197 return resbuf;
2201 /* evaluate next numeric expression in input string */
2202 /* there shoild be no other expressions in the string */
2203 /* returns expression value */
2204 static int32_t getOneExprArg (int *defined, int *addr) {
2205 int32_t res = getExprArg(defined, addr);
2206 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
2207 return res;
2211 /* is next expression a string literal? */
2212 UR_FORCE_INLINE int isStrArg (void) {
2213 const char *s = strSkipSpaces(currLine);
2214 return (s[0] == '"' || s[0] == '\'');
2218 /* check of we reached end of operator */
2219 UR_FORCE_INLINE int isOperatorEnd (void) {
2220 const char *s = strSkipSpaces(currLine);
2221 return (s[0] == 0 || s[0] == ':');
2225 /* check of we reached end of operator */
2226 UR_FORCE_INLINE int isLineEnd (void) {
2227 const char *s = strSkipSpaces(currLine);
2228 return (s[0] == 0 || s[0] == ';');
2232 /* parse string argument from input string */
2233 /* returns parsed string */
2234 static char *getStrArg (int *lenp) {
2235 char *res, qCh;
2236 char *a = strSkipSpaces(currLine);
2237 qCh = *a++;
2238 if (qCh != '"' && qCh != '\'') fatal("string expected");
2239 res = parseStr(&a, qCh, lenp);
2240 if (*a) {
2241 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
2242 memmove(currLine, a, strlen(a)+1);
2243 } else {
2244 currLine[0] = '\0';
2246 return res;
2250 /* parse string argument from input string; allows math after the string */
2251 /* returns parsed string */
2252 /* returns NULL and math expr in `lenp` */
2253 static char *getStrExprArg (int *lenp, int isWord) {
2254 char *a = strSkipSpaces(currLine);
2255 const char qCh = *a++;
2256 if (qCh != '"' && qCh != '\'') fatal("string expected");
2257 char *res = parseStr(&a, qCh, lenp);
2258 if (!res[0]) fatal("invalid empty string");
2259 if (*a) {
2260 if (a[0] != ',' && a[0] != ':' && strlen(res) <= 2) {
2261 memmove(currLine, a, strlen(a)+1);
2262 // "ab" -- 'a' will be in low (first) byte
2263 // 'ab' -- 'a' will be in high (second) byte
2264 int32_t sval;
2265 if (res[1]) {
2266 // two-byte
2267 if (qCh == '"') {
2268 sval = (uint8_t)(res[0]&0xffU)|(((uint8_t)(res[1]&0xffU))<<8);
2269 } else {
2270 sval = (uint8_t)(res[1]&0xffU)|(((uint8_t)(res[0]&0xffU))<<8);
2272 } else {
2273 // one-byte
2274 sval = (uint8_t)(res[0]&0xffU);
2276 //fprintf(stderr, "SMATH:000: str=<%s> (sval=%d); <%s>\n", res, sval, currLine);
2277 // parse math
2278 int defined = 0, fixuptype = UR_FIXUP_NONE;
2279 int32_t xadd = getExprArg(&defined, &fixuptype);
2280 //fprintf(stderr, "SMATH:001: str=<%s> (sval=%d; xadd=%d); <%s>\n", res, sval, xadd, currLine);
2281 if (pass > 0 && !defined) fatal("undefined operand");
2282 sval += xadd;
2283 if (currLine[0] && currLine[0] != ',' && currLine[0] != ':') fatal("bad string expression");
2284 res = NULL;
2285 if (lenp) *lenp = sval;
2286 } else {
2287 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
2288 memmove(currLine, a, strlen(a)+1);
2290 } else {
2291 currLine[0] = '\0';
2293 return res;
2297 /* get identifier (and lowercase it) */
2298 static char *getOneIdArgLo (void) {
2299 static char res[MAX_LINE_SIZE+128];
2300 char *p;
2301 char *a = strSkipSpaces(currLine);
2302 memset(res, 0, sizeof(res));
2303 if (!a[0]) fatal("identifier expected");
2304 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
2305 for (; p > res && isSpace(p[-1]); --p) {}
2306 *p = '\0';
2307 if (p-res > 120) fatal("identifier too long: %s", res);
2308 while (*a && isSpace(*a)) ++a;
2309 if (*a) {
2310 memmove(currLine, a, strlen(a)+1);
2311 if (currLine[0] == ';') currLine[0] = 0;
2312 if (currLine[0]) fatal("extra arguments");
2313 } else {
2314 currLine[0] = '\0';
2316 if (!res[0]) fatal("identifier expected");
2317 for (char *t = res; *t; ++t) *t = toLower(*t);
2318 return res;
2322 /* get label argument */
2323 static char *getLabelArg (int checkdelim) {
2324 static char res[MAX_LINE_SIZE+128];
2325 char *p;
2326 char *a = strSkipSpaces(currLine);
2327 memset(res, 0, sizeof(res));
2328 if (!a[0]) fatal("label expected");
2329 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
2330 for (; p > res && isSpace(p[-1]); --p) {}
2331 *p = '\0';
2332 if (p-res > 120) fatal("label name too long: %s", res);
2333 while (*a && isSpace(*a)) ++a;
2334 if (*a) {
2335 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
2336 memmove(currLine, a, strlen(a)+1);
2337 } else {
2338 currLine[0] = '\0';
2340 if (!res[0]) fatal("label expected");
2341 return res;
2345 /* get label argument, and ensure that it is the last one */
2346 static char *getOneLabelArg (void) {
2347 char *res = getLabelArg(1);
2348 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
2349 return res;
2353 /* returns ',' or 0 */
2354 static char eatComma (void) {
2355 char *a = strSkipSpaces(currLine);
2356 if (!a[0]) { currLine[0] = '\0'; return 0; }
2357 if (a[0] == ':') return 0;
2358 if (a[0] != ',') fatal("invalid expression: ',' expected");
2359 for (++a; *a && isSpace(*a); ++a) {}
2360 if (!a[0]) { currLine[0] = '\0'; return 0; }
2361 memmove(currLine, a, strlen(a)+1);
2362 return ',';
2366 static int checkDelim (char dm) {
2367 char *a = strSkipSpaces(currLine);
2368 if (a[0] != dm) return 0;
2369 for (++a; *a && isSpace(*a); ++a) {}
2370 if (!a[0]) { currLine[0] = '\0'; return 1; }
2371 memmove(currLine, a, strlen(a)+1);
2372 return 1;
2376 static void matchDelim (char dm) {
2377 if (!checkDelim(dm)) fatal("invalid expression: '%c' expected", dm);
2381 static int checkIdentCI (const char *ident) {
2382 ur_assert(ident && ident[0]);
2383 char *a = strSkipSpaces(currLine);
2384 while (*a && *ident) {
2385 char c0 = *a++; if (c0 >= 'A' && c0 <= 'Z') c0 = c0 - 'A' + 'a';
2386 char c1 = *ident++; if (c1 >= 'A' && c1 <= 'Z') c1 = c1 - 'A' + 'a';
2387 if (c0 != c1) return 0;
2389 if (ident[0] != 0) return 0;
2390 if (*a == '_' || isAlphaDigit(*a)) return 0;
2391 while (*a && isSpace(*a)) a += 1;
2392 if (*a) {
2393 memmove(currLine, a, strlen(a)+1);
2394 } else {
2395 currLine[0] = '\0';
2397 return 1;
2401 ///////////////////////////////////////////////////////////////////////////////
2402 // label processor
2404 static MAYBE_UNUSED void removeSpacesAndColons (void) {
2405 char *ep = strSkipSpacesColons(currLine);
2406 memmove(currLine, ep, strlen(ep)+1);
2410 static void checkExprEnd (void) {
2411 char *ep = strSkipSpaces(currLine);
2412 memmove(currLine, ep, strlen(ep)+1);
2413 if (currLine[0] && currLine[0] != ':') fatal("end of expression expected");
2417 static void checkOperatorEnd (void) {
2418 char *ep = strSkipSpaces(currLine);
2419 memmove(currLine, ep, strlen(ep)+1);
2420 if (currLine[0]) fatal("end of operator expected");
2424 /* remove label from curLine */
2425 static void removeLabel (void) {
2426 char *ep = currLine;
2427 if (ep[0] && !isSpace(ep[0])) for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {} // skip text
2428 // skip spaces and colons
2429 ep = strSkipSpacesColons(ep);
2430 memmove(currLine, ep, strlen(ep)+1);
2434 static int labelDoEQU (const char *lblname, const char *value) {
2435 static char n2[256];
2436 UrLabelInfo *lbl;
2438 if (value == NULL || lblname == NULL || !lblname[0] || strlen(lblname) > 255 || strlen(value) >= MAX_LINE_SIZE) return -1;
2439 memset(n2, 0, sizeof(n2));
2440 strcpy(n2, lblname);
2441 if (!urasm_is_valid_name(n2)) return -1; // this is not a valid label, get out of here
2442 // check if this can be an instruction
2443 lbl = urAddLabel(lblname);
2444 if (!lbl->refFile) {
2445 lbl->refLine = 0;
2446 lbl->refFile = strdup("artificially-defined-label");
2448 //fprintf(stderr, "labelDoEQU: <%s>=<%s>\n", lblname, value);
2449 strcpy(currLine, value);
2451 int defined = 1, addr = UR_FIXUP_NONE;
2452 int32_t res = getOneExprArg(&defined, &addr);
2453 lbl->type = LBL_TYPE_EQU;
2454 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2455 if (defined) {
2456 lbl->value = res;
2457 lbl->known = 1;
2458 } else {
2459 return -1; //fatal("can't calculate label %s", lbl->name);
2462 return 0;
2466 static void processLabel (void) {
2467 char *argstart;
2468 char *ep, *nn;
2469 static char n2[256];
2470 UrLabelInfo *lbl;
2471 int noLocAff = 0, doEQU = 0, isTemp = 0;
2472 //fprintf(stderr, "LINE %5d: <%s> from <%s>\n", (currSrcLine ? currSrcLine->lineNo : 0), currLine, (currSrcLine ? currSrcLine->fname : "<nowhere>"));
2473 memset(n2, 0, sizeof(n2));
2475 currSeenLabel[0] = 0;
2477 if (!currLine[0] || isSpace(currLine[0]) || currLine[0] == ':') {
2478 // this may be " id = smth" or " id equ smth"
2479 ep = currLine;
2480 // skip spaces
2481 while (isSpace(*ep)) ++ep;
2482 if (ep[0] == ':' || !ep[0] || (!isAlpha(ep[0]) && ep[0] != '_' && ep[0] != '.' && ep[0] != '@')) {
2483 removeLabel(); // removeLabel() removes any spaces, etc
2484 return;
2486 // this looks like a label; check for '=' or 'equ'
2487 while (isAlphaDigit(ep[0]) || ep[0] == '_' || ep[0] == '.' || ep[0] == '@') ++ep;
2488 nn = ep;
2489 // skip trailing spaces
2490 while (isSpace(*ep)) ++ep;
2491 if (ep[0] == '=') {
2492 doEQU = 0;
2493 argstart = ++ep;
2494 } else if (isSpace(*nn)) {
2495 doEQU = 1;
2496 argstart = strIsCommand("EQU", ep);
2497 } else {
2498 argstart = NULL;
2500 if (!argstart) {
2501 removeLabel(); // removeLabel() removes any spaces, etc
2502 return;
2504 // remove leading spaces from name
2505 // copy label
2506 ep = currLine;
2507 // skip spaces
2508 while (isSpace(*ep)) ++ep;
2509 if (ep >= nn) fatal("internal compiler error");
2510 if (nn-ep > 120) fatal("label too long");
2511 memset(n2, 0, sizeof(n2));
2512 memmove(n2, ep, nn-ep);
2513 if (urFindOp(n2) || !urasm_is_valid_name(n2)) {
2514 //fatal("invalid label name");
2515 removeLabel(); // removeLabel() removes any spaces, etc
2516 return;
2518 // remove label name
2519 memmove(currLine, argstart, strlen(argstart)+1);
2520 // append label
2521 if (n2[0] == '@' && n2[1] == '@' && !n2[2]) {
2522 // unnamed back/forward
2523 fatal("temporary label should end with ':'");
2525 noLocAff = 1;
2526 lbl = addTempLabel();
2528 } else {
2529 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
2530 lbl = findAddLabel(n2);
2532 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2533 if (doEQU && pass == 0 && lbl->type != LBL_TYPE_UNKNOWN) {
2534 fatal("duplicate label '%s'", lbl->name);
2536 int defined = 1, addr = UR_FIXUP_NONE;
2537 int32_t res = getOneExprArg(&defined, &addr);
2538 lbl->type = (doEQU ? LBL_TYPE_EQU : LBL_TYPE_ASS);
2539 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2540 if (defined) {
2541 lbl->value = res;
2542 lbl->known = 1;
2543 } else {
2544 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
2546 currLine[0] = '\0';
2547 return;
2550 // collect label
2551 for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {}
2552 if (ep-currLine > 120) fatal("label too long");
2554 // copy label
2555 memset(n2, 0, sizeof(n2));
2556 memmove(n2, currLine, ep-currLine);
2557 if (urFindOp(n2)) {
2558 ep = strSkipSpaces(ep);
2559 if (*ep != ':') return; // this must be an instruction, process it
2561 if (!urasm_is_valid_name(n2)) return; // this is not a valid label, get out of here
2563 // check for macro
2564 if (findMacro(n2)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
2566 // check if this can be instruction
2567 //ep = strSkipSpaces(ep);
2568 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
2569 // ok, we got a good label
2570 removeLabel();
2571 if (n2[0] == '@' && n2[1] == '@' && !n2[2]) {
2572 // unnamed back/forward
2573 noLocAff = 1; isTemp = 1;
2574 lbl = addTempLabel();
2575 } else {
2576 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
2577 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (currSrcLine ? currSrcLine->lineNo : 0));*/
2578 lbl = findAddLabel(n2);
2579 //printf("new: [%s]\n", lbl->name);
2582 // get command name
2583 if (currLine[0] == '=') {
2584 if (isTemp) fatal("cannot assign values to temp labels");
2585 doEQU = 0;
2586 argstart = currLine+1;
2587 } else {
2588 doEQU = 1;
2589 argstart = strIsCommand("EQU", currLine);
2591 if (!argstart || doEQU) {
2592 if (pass == 0 && !isTemp && lbl->type != LBL_TYPE_UNKNOWN) {
2593 fatal("duplicate label '%s'", lbl->name);
2597 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
2598 if (argstart) {
2599 if (isTemp) fatal("cannot assign values to temp labels");
2600 // do '=' or 'EQU'
2601 memmove(currLine, argstart, strlen(argstart)+1);
2602 if (!doEQU) {
2603 if (lbl->type != LBL_TYPE_UNKNOWN && lbl->type != LBL_TYPE_ASS) {
2604 fatal("duplicate label '%s'", lbl->name);
2607 int defined = 1, addr = UR_FIXUP_NONE;
2608 int32_t res = getOneExprArg(&defined, &addr);
2609 lbl->type = (doEQU ? LBL_TYPE_EQU : LBL_TYPE_ASS);
2610 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
2611 if (defined) {
2612 lbl->value = res;
2613 lbl->known = 1;
2614 } else {
2615 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
2617 currLine[0] = '\0';
2618 return;
2621 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
2622 // code label
2623 // update last seen global
2624 if (!isTemp) {
2625 if (lbl->name[0] != '{' && lbl->name[0] != '.' && !noLocAff) {
2626 //fprintf(stderr, "**: LASTGLOB: <%s>\n", lbl->name);
2627 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
2628 lastSeenGlobalLabel = strdup(lbl->name);
2630 lbl->type = LBL_TYPE_CODE;
2631 lbl->value = disp;
2632 lbl->known = 1;
2633 lbl->fixuptype = UR_FIXUP_WORD;
2635 snprintf(currSeenLabel, sizeof(currSeenLabel), "%s", n2);
2636 } else if (lbl->type != LBL_TYPE_UNKNOWN) {
2637 ur_assert(lbl->type == LBL_TYPE_CODE);
2638 ur_assert(lbl->value == disp);
2639 lbl->tempFwd = 0;
2640 } else {
2641 //fprintf(stderr, "FIXTEMP: %s\n", lbl->name);
2642 lbl->type = LBL_TYPE_CODE;
2643 lbl->value = disp;
2644 lbl->known = 1;
2645 lbl->fixuptype = UR_FIXUP_WORD;
2646 ur_assert(lbl->tempIdx != 0);
2647 lbl->tempFwd = 0;
2652 ///////////////////////////////////////////////////////////////////////////////
2653 // instruction finder (in source)
2655 /* array ends with NULL */
2656 /* returns line or NULL */
2657 /* iidx will be set to found instruction number */
2658 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
2659 if (iidx) *iidx = -1;
2660 for (SourceLine *cur = currSrcLine; cur; cur = cur->next) {
2661 va_list ap;
2662 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
2663 va_start(ap, iidx);
2664 for (int f = 0; ;++f) {
2665 const char *name = va_arg(ap, const char *);
2667 if (!name) break;
2668 if (strIsCommand(name, cur->line)) {
2669 va_end(ap);
2670 if (iidx) *iidx = f;
2671 return cur;
2674 va_end(ap);
2676 return NULL;
2680 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
2681 return findNextInstructionFromList(NULL, name, NULL);
2685 ///////////////////////////////////////////////////////////////////////////////
2686 // writers
2688 ///////////////////////////////////////////////////////////////////////////////
2689 static void writeFixups (void) {
2690 void writeFixupList (FILE *fo, int cnt, const char *lbl, int (*chk)(const FixupItem *)) {
2691 if (cnt > 0) {
2692 int prevaddr = 0;
2693 fprintf(fo, "%s:\n", lbl);
2694 if (optFixupType == 1) fprintf(fo, " dw %d ; count\n", cnt);
2695 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2696 if (chk(fx)) {
2697 fprintf(fo, " dw #%04X\n", fx->opaddr-prevaddr);
2698 if (optFixupType == 2) {
2699 prevaddr = fx->opaddr;
2703 if (optFixupType == 2 || optFixupType == 3) fprintf(fo, " dw 0 ; end marker\n");
2707 if (optFixupType == 0) {
2708 /* simple text file */
2709 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.txt");
2710 printf("writing fixups to '%s'...\n", fname);
2711 FILE *fo = fopen(fname, "w");
2712 free(fname);
2713 if (fo == NULL) fatal("can't write fixup file");
2714 fprintf(fo, "; addr dadr sz\n");
2715 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2716 static const char type[4] = "NWLH";
2717 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
2719 fclose(fo);
2720 } else {
2721 /* various asm formats */
2722 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.zas");
2723 printf("writing fixups to '%s'...\n", fname);
2724 FILE *fo = fopen(fname, "w");
2725 int cntw = 0, cntwl = 0, cntwh = 0, cntbl = 0, cntbh = 0;
2726 free(fname);
2727 if (fo == NULL) fatal("can't write fixup file");
2728 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
2729 if (fx->fixuptype == UR_FIXUP_WORD) { ++cntw; continue; }
2730 switch (fx->fixuptype) {
2731 case UR_FIXUP_LOBYTE: if (fx->size == 2) ++cntwl; else ++cntbl; break;
2732 case UR_FIXUP_HIBYTE: if (fx->size == 2) ++cntwh; else ++cntbh; break;
2735 writeFixupList(fo, cntw, "fixup_table_w", lambda(int, (const FixupItem *fx) {
2736 return (fx->fixuptype == UR_FIXUP_WORD);
2737 }));
2738 writeFixupList(fo, cntwl, "fixup_table_wl", lambda(int, (const FixupItem *fx) {
2739 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 2);
2740 }));
2741 writeFixupList(fo, cntwh, "fixup_table_wh", lambda(int, (const FixupItem *fx) {
2742 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 2);
2743 }));
2744 writeFixupList(fo, cntbl, "fixup_table_bl", lambda(int, (const FixupItem *fx) {
2745 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 1);
2746 }));
2747 writeFixupList(fo, cntbh, "fixup_table_bh", lambda(int, (const FixupItem *fx) {
2748 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 1);
2749 }));
2750 fclose(fo);
2755 ///////////////////////////////////////////////////////////////////////////////
2756 /* return 'found' flag */
2757 static int findChunkFrom (int addr, int *start, int *len) {
2758 if (addr < 0) addr = 0;
2759 for (; addr <= 65535 && !memused[addr]; ++addr) {}
2760 if (addr > 65535) return 0;
2761 *start = addr;
2762 for (; addr <= 65535 && memused[addr]; ++addr) {}
2763 *len = addr-(*start);
2764 return 1;
2768 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
2769 for (; buflen >= 2; buflen -= 2) {
2770 int cnt = *buf++;
2771 uint8_t byte = *buf++;
2773 if (cnt == 0) {
2774 // copy
2775 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
2776 if (!memused[addr]) putByte(addr, *buf);
2777 if (addr == 0xffff) return;
2779 } else {
2780 // fill
2781 for (; cnt > 0; --cnt, ++addr) {
2782 if (!memused[addr]) putByte(addr, byte);
2783 if (addr == 0xffff) return;
2790 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
2791 if (fwrite(&b, 1, 1, fo) != 1) return -1;
2792 if (bxor) *bxor = (*bxor)^b;
2793 return 0;
2797 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
2798 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
2799 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
2800 return 0;
2804 ///////////////////////////////////////////////////////////////////////////////
2805 // .sna
2807 static int saveSna (const char *fname, int as48) {
2808 char *fn;// = malloc(strlen(fname)+16);
2809 uint8_t regs[27];
2810 FILE *fo;
2811 char abuf[32];
2813 //fn = strprintf("%s/%s.sna", optOutputDir, fname);
2814 fn = buildOutputFileName(fname, "sna");
2815 fo = fopen(fn, "wb");
2816 free(fn);
2817 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
2818 printf("out: %s.sna\n", fname);
2819 memmove(regs, ursna48, 27);
2820 #if 0
2821 if (optRunSNA) {
2822 /* push new address */
2823 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
2824 sp -= 2;
2825 putByte(sp, ent&0xFFU);
2826 putByte(sp+1, (ent>>8)&0xFFU);
2827 regs[23] = sp&0xFFU;
2828 regs[24] = (sp>>8)&0xFFU;
2830 fwrite(regs, 27, 1, fo);
2831 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
2832 fwrite(memory+16384, 49152, 1, fo);
2833 #endif
2835 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
2837 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
2838 //fprintf(stderr, "%d\n", memused[sp]);
2839 if (memused[sp] || memused[(sp+1)&0xffff]) {
2840 fprintf(stderr, "FATAL: can't save snapshot!\n");
2841 goto error;
2844 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
2845 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
2846 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
2848 sprintf(abuf, "%05d", clrAddr);
2849 memcpy(memory+23762, abuf, 5);
2850 sprintf(abuf, "%05d", ent);
2851 memcpy(memory+23773, abuf, 5);
2853 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
2855 if (!as48) {
2856 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
2858 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
2859 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
2860 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
2861 // write pages
2862 memset(memory, 0, 16384);
2863 for (int f = 1; f < 8; ++f) {
2864 if (f != 2 && f != 5) {
2865 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
2870 fclose(fo);
2871 return 0;
2872 error:
2873 fclose(fo);
2874 unlink(fname);
2875 return -1;
2879 ///////////////////////////////////////////////////////////////////////////////
2880 // bin chunks
2882 static void saveRaw (const char *basename) {
2883 char *fname = NULL;// = malloc(strlen(basename)+16);
2884 int start = 0, len;
2885 FILE *fo;
2887 while (findChunkFrom(start, &start, &len)) {
2888 if (fname != NULL) free(fname);
2889 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, (optTapExt ? "tap" : "bin"));
2890 fo = fopen(fname, "wb");
2891 if (!fo) {
2892 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
2893 } else {
2894 printf("out: %s\n", fname);
2895 if (fwrite(memory+start, len, 1, fo) != 1) {
2896 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2897 fclose(fo);
2898 unlink(fname);
2899 } else {
2900 fclose(fo);
2903 start += len;
2905 if (fname != NULL) free(fname);
2909 ///////////////////////////////////////////////////////////////////////////////
2910 // HoBeta files
2912 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
2913 char name[8];
2914 char type;
2915 uint16_t start;
2916 uint16_t len;
2917 uint8_t zero; // always zero
2918 uint8_t secLen; // length in sectors
2919 uint16_t checksum;
2920 } HOBHeader;
2923 static uint16_t calcHobSum (const void *hdr) {
2924 const uint8_t *buf = (const uint8_t *)hdr;
2925 uint16_t res = 0;
2927 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
2928 return res;
2932 static void saveHob (const char *basename) {
2933 char *fname = NULL;//malloc(strlen(basename)+16);
2934 int start = 0, len;
2935 HOBHeader hdr;
2936 FILE *fo;
2938 while (findChunkFrom(start, &start, &len)) {
2939 if (fname != NULL) free(fname);
2940 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, "$C");
2941 fo = fopen(fname, "wb");
2942 if (!fo) {
2943 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
2944 start += len;
2945 } else {
2946 char tmpbuf[sizeof(hdr.name)*2];
2947 printf("out: %s\n", fname);
2948 memset(&hdr, 0, sizeof(hdr));
2949 memset(&hdr.name, 32, 8);
2950 snprintf(tmpbuf, sizeof(tmpbuf), "%c%04X", basename[0], start);
2951 while (strlen(tmpbuf) < sizeof(hdr.name)) strcat(tmpbuf, " "); /* sorry! */
2952 memcpy(hdr.name, tmpbuf, sizeof(hdr.name));
2953 hdr.type = 'C';
2954 hdr.start = start;
2955 hdr.len = len;
2956 hdr.secLen = (len+255)/256;
2957 hdr.checksum = calcHobSum(&hdr);
2958 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
2959 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2960 fclose(fo);
2961 unlink(fname);
2962 goto quit;
2964 if (fwrite(memory+start, len, 1, fo) != 1) {
2965 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2966 fclose(fo);
2967 unlink(fname);
2968 goto quit;
2970 start += len;
2971 while (len%256) {
2972 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
2973 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
2974 fclose(fo);
2975 unlink(fname);
2976 goto quit;
2978 ++len;
2979 //fprintf(stderr, ":%d\n", len%256);
2981 fclose(fo);
2984 quit:
2985 if (fname != NULL) free(fname);
2989 // ////////////////////////////////////////////////////////////////////////// //
2990 typedef struct {
2991 uint8_t fcount;
2992 uint8_t dir[14*256];
2993 uint32_t dirpos;
2994 uint32_t currfstartpos;
2995 uint8_t *data;
2996 size_t datapos;
2997 size_t datasize;
2998 } SCLFile;
3001 static const uint8_t monoldr_code[169] = {
3002 0x00,0x00,0xff,0xf0,0xff,0xf0,0xff,0xf0,0xff,0xf3,0x01,0xfd,0x7f,0x3e,0x10,0xed,
3003 0x79,0xaf,0x21,0x00,0x58,0x11,0x01,0x58,0x01,0xff,0x02,0xfb,0x76,0xf3,0xd3,0xfe,
3004 0x36,0x00,0xed,0xb0,0x21,0x00,0x40,0x11,0x01,0x40,0x01,0x00,0x18,0x77,0xed,0xb0,
3005 0xfb,0x76,0xf3,0x3b,0x3b,0xe1,0xe5,0x11,0xce,0xff,0x19,0x7e,0x23,0x5e,0x23,0x56,
3006 0x23,0xc1,0xe5,0xd5,0x69,0x60,0x01,0x35,0x00,0x09,0x4f,0x87,0x81,0x01,0x43,0x00,
3007 0x81,0x4f,0x3e,0x00,0x88,0x47,0xed,0xb0,0xd1,0xe1,0xd5,0x01,0xff,0x03,0x13,0xed,
3008 0xa0,0xed,0xa0,0x13,0x10,0xf8,0xc9,0x31,0xa5,0xa5,0xfb,0x21,0x5a,0x5a,0xe5,0x21,
3009 0x9a,0x02,0xe5,0x76,0x3b,0x3b,0xe1,0x01,0x35,0x00,0x09,0x0e,0x00,0x7e,0xb7,0x28,
3010 0x23,0x23,0x5e,0x23,0x56,0x23,0xe5,0xeb,0x06,0x08,0xb8,0x30,0x01,0x47,0x90,0xf5,
3011 0xc5,0xe5,0xed,0x5b,0xf4,0x5c,0x0e,0x05,0xcd,0x13,0x3d,0xe1,0xc1,0x09,0xf1,0x20,
3012 0xe7,0xe1,0x18,0xd9,0xe1,0xd1,0xf9,0xeb,0xe9,
3016 static void sclInit (SCLFile *scl) {
3017 memset(scl, 0, sizeof(*scl));
3018 scl->datasize = 2*80*16*256; /* maximum disk size */
3019 scl->data = malloc(scl->datasize);
3020 memset(scl->data, 0, scl->datasize);
3021 scl->currfstartpos = 0xffffffffu;
3025 static void sclClear (SCLFile *scl) {
3026 if (!scl) return;
3027 if (scl->data) free(scl->data);
3028 memset(scl, 0, sizeof(*scl));
3029 scl->currfstartpos = 0xffffffffu;
3033 static void sclStartFile (SCLFile *scl, const char *name, char type, uint16_t v0, uint16_t v1) {
3034 if (scl->fcount == 255) {
3035 fprintf(stderr, "FATAL: too many files in SCL!\n");
3036 exit(1);
3038 if (scl->currfstartpos != 0xffffffffu) {
3039 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
3040 exit(1);
3042 int nlen = 0;
3043 while (nlen < 8 && name && name[nlen]) scl->dir[scl->dirpos++] = name[nlen++];
3044 while (nlen < 8) { scl->dir[scl->dirpos++] = ' '; ++nlen; }
3045 scl->dir[scl->dirpos++] = type;
3046 scl->dir[scl->dirpos++] = v0&0xff;
3047 scl->dir[scl->dirpos++] = (v0>>8)&0xff;
3048 scl->dir[scl->dirpos++] = v1&0xff;
3049 scl->dir[scl->dirpos++] = (v1>>8)&0xff;
3050 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
3051 scl->currfstartpos = scl->datapos;
3055 static void sclEndFile (SCLFile *scl) {
3056 if (scl->currfstartpos == 0xffffffffu) {
3057 fprintf(stderr, "FATAL: last SCL file already finished!\n");
3058 exit(1);
3060 /* align to sector */
3061 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3062 const uint32_t fsz = scl->datapos-scl->currfstartpos;
3063 if (fsz > 255*256) {
3064 fprintf(stderr, "FATAL: SCL file too big!\n");
3065 exit(1);
3067 ur_assert((fsz&255) == 0);
3068 scl->dir[scl->dirpos++] = fsz/256; /* size in sectors */
3069 ur_assert(scl->dirpos%14 == 0);
3070 ++scl->fcount;
3071 scl->currfstartpos = 0xffffffffu;
3075 static void sclWriteDataForce (SCLFile *scl, const void *buf, size_t len) {
3076 if (!len) return;
3077 if (len > 255*256) {
3078 fprintf(stderr, "FATAL: SCL file too big!\n");
3079 exit(1);
3081 memcpy(scl->data+scl->datapos, buf, len);
3082 scl->datapos += len;
3086 static void sclWriteData (SCLFile *scl, const void *buf, size_t len) {
3087 if (scl->currfstartpos == 0xffffffffu) {
3088 fprintf(stderr, "FATAL: no open SCL file!\n");
3089 exit(1);
3091 sclWriteDataForce(scl, buf, len);
3095 static void sclWriteByte (SCLFile *scl, uint8_t b) {
3096 sclWriteData(scl, &b, 1);
3100 static void sclWriteWord (SCLFile *scl, uint16_t w) {
3101 uint8_t b = w&0xff;
3102 sclWriteData(scl, &b, 1);
3103 b = (w>>8)&0xff;
3104 sclWriteData(scl, &b, 1);
3108 static int sclFileWrite (FILE *fo, const void *buf, size_t count, uint32_t *checksum) {
3109 if (!count) return 0;
3110 const uint8_t *p = (const uint8_t *)buf;
3111 if (checksum) for (size_t n = count; n--; ++p) *checksum += (uint32_t)(*p);
3112 if (fwrite(buf, count, 1, fo) != 1) return -1;
3113 return 0;
3117 // <0: error; 0: ok
3118 static int sclSaveToFile (FILE *fo, SCLFile *scl) {
3119 if (scl->currfstartpos != 0xffffffffu) {
3120 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
3121 exit(1);
3123 const char *sign = "SINCLAIR";
3124 uint32_t checksum = 0;
3125 // header
3126 if (sclFileWrite(fo, sign, 8, &checksum) != 0) return -1;
3127 if (sclFileWrite(fo, &scl->fcount, 1, &checksum) != 0) return -1;
3128 // directory
3129 if (sclFileWrite(fo, scl->dir, scl->dirpos, &checksum) != 0) return -1;
3130 // data
3131 if (sclFileWrite(fo, scl->data, scl->datapos, &checksum) != 0) return -1;
3132 // write checksum
3133 for (unsigned f = 0; f < 4; ++f) {
3134 const uint8_t b = (checksum>>(f*8))&0xff;
3135 if (fwrite(&b, 1, 1, fo) != 1) return -1;
3137 // done
3138 return 0;
3142 // ////////////////////////////////////////////////////////////////////////// //
3143 static void saveSCLCargador (SCLFile *scl) {
3144 static uint8_t cargador[16384]; // should be enough for everyone
3145 int linestart = -1;
3146 int linenum = 0;
3147 int start = 0, len, pos;
3149 void putStr (const char *s) {
3150 for (; *s; ++s) cargador[pos++] = *s;
3153 void putNum (int num) {
3154 char buf[64];
3155 sprintf(buf, "%d", num);
3156 putStr(buf);
3159 void startLine () {
3160 ++linenum;
3161 linestart = pos;
3162 // number
3163 cargador[pos++] = (linenum>>8)&0xff;
3164 cargador[pos++] = linenum&0xff;
3165 // size (will be fixed later)
3166 cargador[pos++] = 0;
3167 cargador[pos++] = 0;
3170 void flushLine () {
3171 if (linestart >= 0) {
3172 const int size = pos-linestart-4;
3173 cargador[linestart+2] = size&0xff;
3174 cargador[linestart+3] = (size>>8)&0xff;
3176 linestart = -1;
3179 char cname[16];
3181 pos = 0;
3182 startLine();
3183 // CLEAR VAL "xxx"
3184 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
3186 // load blocks
3187 int cont = 1;
3188 while (findChunkFrom(start, &start, &len)) {
3189 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3190 if (cont) { putStr(":"); cont = 0; }
3191 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3192 // generate chunk name
3193 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
3194 putStr(cname);
3195 //" CODE
3196 putStr("\"\xaf\r");
3197 flushLine();
3198 startLine();
3199 start += len;
3202 // load code files
3203 for (OutDataFile *off = datafiles; off; off = off->next) {
3204 if (off->type == 1) continue; // skip +3DOS bootsector
3205 if (off->type != 'C') continue;
3206 if (!off->hasstart) continue;
3207 // :RANDOMIZE USR VAL "15619":REM:LOAD "
3208 if (cont) { putStr(":"); cont = 0; }
3209 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
3210 // generate chunk name
3211 snprintf(cname, sizeof(cname), "%s", off->name);
3212 char *dotpos = strchr(cname, '.');
3213 char type = 'C';
3214 if (dotpos) { type = toUpper(dotpos[1]); *dotpos = 0; }
3215 if (type < 'A' || type > 'Z') type = 'C';
3216 for (char *s = cname; *s; ++s) *s = toUpper(*s);
3217 putStr(cname);
3218 //" CODE
3219 putStr("\"\xaf\r");
3220 flushLine();
3221 startLine();
3224 // and run
3225 if (cont) { putStr(":"); cont = 0; }
3226 // RANDOMIZE USR VAL "xxx"
3227 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"");
3228 putStr("\r");
3229 flushLine();
3231 //putWord(0xaa80);
3232 //putWord(1); // autostart line
3234 // write to SCL
3235 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
3236 sclWriteData(scl, cargador, (size_t)pos);
3237 sclWriteByte(scl, 0x80);
3238 sclWriteByte(scl, 0xaa);
3239 sclWriteWord(scl, 1);
3240 sclEndFile(scl);
3244 static void saveSCLCargadorMono (SCLFile *scl) {
3245 static uint8_t cargador[16384]; // should be enough for everyone
3246 int linestart = -1;
3247 int linenum = 0;
3248 int start = 0, len, pos;
3250 void putDB (uint8_t v) {
3251 cargador[pos++] = v;
3254 void putDW (uint16_t v) {
3255 cargador[pos++] = v&0xff;
3256 cargador[pos++] = (v>>8)&0xff;
3259 void putStr (const char *s) {
3260 for (; *s; ++s) cargador[pos++] = *s;
3263 void putNum (int num) {
3264 char buf[64];
3265 sprintf(buf, "%d", num);
3266 putStr(buf);
3269 void startLine () {
3270 linestart = pos;
3271 // number
3272 cargador[pos++] = (linenum>>8)&0xff;
3273 cargador[pos++] = linenum&0xff;
3274 // size (will be fixed later)
3275 cargador[pos++] = 0;
3276 cargador[pos++] = 0;
3277 linenum++;
3280 void flushLine () {
3281 if (linestart >= 0) {
3282 const int size = pos-linestart-4;
3283 cargador[linestart+2] = size&0xff;
3284 cargador[linestart+3] = (size>>8)&0xff;
3286 linestart = -1;
3289 pos = 0;
3290 startLine();
3292 // 0 REM code
3293 putStr("\xea");
3294 // monoloader parameters:
3295 // dw ldr_copy_addr
3296 // dw ldr_sp
3297 // dw prg_start_addr
3298 // dw prg_sp
3299 // block count
3300 const int bcountpos = pos;
3301 putDB(0);
3302 putDW(0x4000); // to screen$
3303 putDW(0x4200); // stack
3304 putDW(ent); // program start address
3305 putDW(0x0000); // stack
3306 // copy monoloader
3307 for (uint32_t f = 9; f < (uint32_t)sizeof(monoldr_code); ++f) {
3308 putDB(monoldr_code[f]);
3310 // put block data
3311 while (findChunkFrom(start, &start, &len)) {
3312 if (len > 255*256) fatal("code chunk too big");
3313 // sector count
3314 putDB(len/256+(len%256 ? 1 : 0));
3315 // address
3316 putDW(start);
3317 start += len;
3318 // one more block
3319 ++cargador[bcountpos];
3321 // load code files
3322 for (OutDataFile *off = datafiles; off; off = off->next) {
3323 if (off->type == 1) continue; // skip +3DOS bootsector
3324 if (off->type != 'C') continue;
3325 if (!off->hasstart) continue;
3326 if (off->size > 255*256) fatal("data chunk too big");
3327 // sector count
3328 putDB(off->size/256+(off->size%256 ? 1 : 0));
3329 // address
3330 putDW(off->start);
3331 // one more block
3332 ++cargador[bcountpos];
3334 // no more blocks
3335 putDB(0);
3337 flushLine();
3338 startLine();
3340 // CLEAR VAL "xxx":RANDOMIZE USR PEEK VAL "23635"+VAL "256"*PEEK VAL "23636"+VAL "14"
3341 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\":");
3342 putStr("\xf9\xc0(\xbe\xb0\"23635\"+\xb0\"256\"*\xbe\xb0\"23636\"+\xb0\"14\")\r");
3343 flushLine();
3345 // write to SCL
3346 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
3347 sclWriteData(scl, cargador, (size_t)pos);
3348 sclWriteByte(scl, 0x80);
3349 sclWriteByte(scl, 0xaa);
3350 sclWriteWord(scl, 1);
3351 sclEndFile(scl);
3353 // put block data
3354 uint8_t ssz = 0;
3355 start = 0;
3356 while (findChunkFrom(start, &start, &len)) {
3357 ssz += len/256+(len%256 ? 1 : 0);
3358 sclWriteDataForce(scl, memory+start, len);
3359 /* align to sector */
3360 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3361 start += len;
3363 for (unsigned dphase = 0; dphase < 2; ++dphase) {
3364 // put code files
3365 for (OutDataFile *off = datafiles; off; off = off->next) {
3366 if (off->type == 1) continue; // skip +3DOS bootsector
3367 if (off->type != 'C') continue;
3368 if (dphase == 0) {
3369 if (!off->hasstart) continue;
3370 } else {
3371 if (off->hasstart) continue;
3373 if (off->size > 255*256) fatal("data chunk too big");
3374 ssz += off->size/256+(off->size%256 ? 1 : 0);
3375 sclWriteDataForce(scl, off->data, off->size);
3376 /* align to sector */
3377 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
3381 // fix basic file sector size
3382 scl->dir[scl->dirpos-1] += ssz;
3386 static void saveSCL (const char *basename) {
3387 SCLFile scl;
3388 int fcount = 0;
3389 int start, len;
3390 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3391 char *fname = buildOutputFileName(basename, "scl");
3393 // count files
3394 start = 0;
3395 while (findChunkFrom(start, &start, &len)) {
3396 ++fcount;
3397 start += len;
3399 // count data files
3400 for (OutDataFile *off = datafiles; off; off = off->next) {
3401 if (off->type == 1) continue; // skip +3DOS bootsector
3402 if (off->type == 'C') ++fcount;
3405 if (fcount && optRunSCL) fcount += (optRunSCL > 0 ? 2 : 1); // +loader and boot
3406 if (fcount > 255) {
3407 fprintf(stderr, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname, fcount);
3408 exit(1);
3411 // create output file
3412 FILE *fo = fopen(fname, "wb");
3413 if (!fo) {
3414 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
3415 exit(1);
3418 // initialise SCL writer
3419 sclInit(&scl);
3420 // write loader
3421 if (fcount && optRunSCL) {
3422 // create simple boot
3423 if (optRunSCL > 0) {
3424 const uint8_t dasboot[] = {
3425 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"
3427 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
3428 sclWriteWord(&scl, 1); // line number
3429 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
3430 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
3431 // TR-DOS signature
3432 sclWriteByte(&scl, 0x80);
3433 sclWriteByte(&scl, 0xaa);
3434 // start line
3435 sclWriteWord(&scl, 0);
3436 sclEndFile(&scl);
3438 saveSCLCargador(&scl);
3441 // write chunks
3442 char cname[16];
3443 start = 0;
3444 while (findChunkFrom(start, &start, &len)) {
3445 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
3446 sclStartFile(&scl, cname, 'C', (unsigned)start, (unsigned)len);
3447 sclWriteData(&scl, memory+(unsigned)start, (unsigned)len);
3448 sclEndFile(&scl);
3449 start += len;
3452 // write data files
3453 for (OutDataFile *off = datafiles; off; off = off->next) {
3454 if (off->type == 1) continue; // skip +3DOS bootsector
3455 if (off->type != 'C') {
3456 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3457 continue;
3459 snprintf(cname, sizeof(cname), "%s", off->name);
3460 char *dotpos = strchr(cname, '.');
3461 char type = 'C';
3462 if (dotpos) { type = toUpper(dotpos[1]); *dotpos = 0; }
3463 if (type < 'A' || type > 'Z') type = 'C';
3464 for (char *s = cname; *s; ++s) *s = toUpper(*s);
3465 sclStartFile(&scl, cname, type, (unsigned)off->start, (unsigned)off->size);
3466 sclWriteData(&scl, off->data, (unsigned)off->size);
3467 sclEndFile(&scl);
3470 if (sclSaveToFile(fo, &scl) < 0) {
3471 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
3472 fclose(fo);
3473 unlink(fname);
3474 exit(1);
3476 sclClear(&scl);
3477 fclose(fo);
3478 printf("out: %s\n", fname);
3479 if (fname != NULL) free(fname);
3483 static void saveSCLMono (const char *basename) {
3484 SCLFile scl;
3485 int start, len;
3486 //char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
3487 char *fname = buildOutputFileName(basename, "scl");
3489 // count total size in sectors
3490 int secsize = 1; // for loader
3491 start = 0;
3492 while (findChunkFrom(start, &start, &len)) {
3493 secsize += len/256+(len%256 ? 1 : 0);
3494 start += len;
3496 // count data files
3497 for (OutDataFile *off = datafiles; off; off = off->next) {
3498 if (off->type == 1) continue; // skip +3DOS bootsector
3499 if (off->type == 'C') {
3500 secsize += off->size/256+(off->size%256 ? 1 : 0);
3504 if (secsize > 254) {
3505 fprintf(stderr, "ERROR: can't write file '%s' (too many sectors: %d)!\n", fname, secsize);
3506 exit(1);
3509 // create output file
3510 FILE *fo = fopen(fname, "wb");
3511 if (!fo) {
3512 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
3513 exit(1);
3516 // initialise SCL writer
3517 sclInit(&scl);
3518 // write loader
3519 if (optRunSCL) {
3520 // create simple boot
3521 if (optRunSCL > 0) {
3522 const uint8_t dasboot[] = {
3523 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"
3525 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
3526 sclWriteWord(&scl, 1); // line number
3527 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
3528 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
3529 // TR-DOS signature
3530 sclWriteByte(&scl, 0x80);
3531 sclWriteByte(&scl, 0xaa);
3532 // start line
3533 sclWriteWord(&scl, 0);
3534 sclEndFile(&scl);
3537 saveSCLCargadorMono(&scl);
3539 if (sclSaveToFile(fo, &scl) < 0) {
3540 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
3541 fclose(fo);
3542 unlink(fname);
3543 exit(1);
3545 sclClear(&scl);
3546 fclose(fo);
3547 printf("out: %s\n", fname);
3548 if (fname != NULL) free(fname);
3552 // ////////////////////////////////////////////////////////////////////////// //
3553 int optRunDSK = 0;
3554 int optDskType = P3DSK_PCW_SS;
3557 static int saveDSKCargador (P3DiskInfo *p3d, int autorun) {
3558 static uint8_t cargador[16384]; // should be enough for everyone
3559 int linestart = -1;
3560 int linenum = 0;
3561 int start = 0, len, pos;
3563 void putStr (const char *s) {
3564 for (; *s; ++s) cargador[pos++] = *s;
3567 void putNum (int num) {
3568 char buf[64];
3569 sprintf(buf, "%d", num);
3570 putStr(buf);
3573 void startLine () {
3574 ++linenum;
3575 linestart = pos;
3576 // number
3577 cargador[pos++] = (linenum>>8)&0xff;
3578 cargador[pos++] = linenum&0xff;
3579 // size (will be fixed later)
3580 cargador[pos++] = 0;
3581 cargador[pos++] = 0;
3584 void flushLine () {
3585 if (linestart >= 0) {
3586 const int size = pos-linestart-4;
3587 cargador[linestart+2] = size&0xff;
3588 cargador[linestart+3] = (size>>8)&0xff;
3590 linestart = -1;
3593 char cname[16];
3595 pos = 0;
3596 startLine();
3597 // CLEAR VAL "xxx"
3598 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
3599 // load blocks
3600 while (findChunkFrom(start, &start, &len)) {
3601 // :LOAD "
3602 putStr(":\xef\"");
3603 // generate chunk name
3604 snprintf(cname, sizeof(cname), "C%04X.BIN", (unsigned)start);
3605 putStr(cname);
3606 // " CODE
3607 putStr("\"\xaf");
3608 start += len;
3610 putStr("\r");
3611 flushLine();
3612 startLine();
3613 // and run
3614 // :RANDOMIZE USR VAL "xxx"
3615 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
3616 flushLine();
3618 // write to DSK
3619 // build cp/m 8.3 name
3620 char pdfname[16];
3621 if (autorun) strcpy(pdfname, "DISK"); else strcpy(pdfname, "CARGADOR.BAS");
3623 int crerr = p3dskCreateFile(p3d, pdfname);
3624 if (crerr < 0) {
3625 if (crerr == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3626 if (crerr != FLPERR_FILEEXIST) { fprintf(stderr, "+3DOS disk error (creating)\n"); return -1; }
3627 p3dskDeleteFiles(p3d, pdfname);
3628 crerr = p3dskCreateFile(p3d, pdfname);
3629 if (crerr < 0) { fprintf(stderr, "+3DOS disk error (creating1)\n"); return -1; }
3632 P3DskFileInfo nfo;
3633 if (p3dskOpenFile(p3d, &nfo, pdfname) != FLPERR_OK) { fprintf(stderr, "cannot open created +3DOS file '%s'\n", pdfname); return -1; }
3635 int wrres = p3dskWriteFile(&nfo, cargador, 128, (int)pos);
3636 if (wrres == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3637 if (wrres == FLPERR_NOSPACE) { fprintf(stderr, "NO FREE SPACE (writing)!\n"); return -1; }
3638 if (wrres < 0) { fprintf(stderr, "+3DOS disk writing error (writing)\n"); return -1; }
3640 // write +3DOS header
3641 P3DskFileHeader hdr;
3642 memset(&hdr, 0, sizeof(P3DskFileHeader));
3643 hdr.valid = 1;
3644 hdr.issue = 1;
3645 hdr.version = 0;
3646 hdr.filesize = (uint32_t)(pos+128);
3647 hdr.bastype = 0; // basic
3648 hdr.baslength = (uint16_t)pos;
3649 hdr.basaddr = 1;
3650 hdr.basvarsofs = (uint16_t)pos;
3651 if (p3dskWriteFileHeader(&nfo, &hdr) != FLPERR_OK) { fprintf(stderr, "error writing +3DOS file header\n"); return -1; }
3653 return 0;
3657 static int saveDSKChunk (P3DiskInfo *p3d, const char *name, const void *data, int start, int len, int p3header) {
3658 // write to DSK
3659 // build cp/m 8.3 name
3660 char pdfname[13];
3661 if (p3dskNormaliseFileName(pdfname, name) != FLPERR_OK) { fprintf(stderr, "bad +3DOS file name '%s'\n", name); return -1; }
3663 for (char *s = pdfname; *s; ++s) if (*s >= 'a' && *s <= 'z') s[0] -= 32;
3665 int crerr = p3dskCreateFile(p3d, pdfname);
3666 if (crerr < 0) {
3667 if (crerr == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (creating)!\n"); return -1; }
3668 if (crerr != FLPERR_FILEEXIST) { fprintf(stderr, "+3DOS disk error (creating)\n"); return -1; }
3669 p3dskDeleteFiles(p3d, pdfname);
3670 crerr = p3dskCreateFile(p3d, pdfname);
3671 if (crerr < 0) { fprintf(stderr, "+3DOS disk error (creating1)\n"); return -1; }
3674 P3DskFileInfo nfo;
3675 if (p3dskOpenFile(p3d, &nfo, pdfname) != FLPERR_OK) { fprintf(stderr, "cannot open created +3DOS file '%s'\n", pdfname); return -1; }
3677 int wrres = p3dskWriteFile(&nfo, data, (p3header ? 128 : 0), (int)len);
3678 if (wrres == FLPERR_MANYFILES) { fprintf(stderr, "NO FREE DIRECTORY ENTRIES (writing)!\n"); return -1; }
3679 if (wrres == FLPERR_NOSPACE) { fprintf(stderr, "NO FREE SPACE (writing)!\n"); return -1; }
3680 if (wrres < 0) { fprintf(stderr, "+3DOS disk writing error (writing)\n"); return -1; }
3682 // write +3DOS header
3683 if (p3header) {
3684 P3DskFileHeader hdr;
3685 memset(&hdr, 0, sizeof(P3DskFileHeader));
3686 hdr.valid = 1;
3687 hdr.issue = 1;
3688 hdr.version = 0;
3689 hdr.filesize = (uint32_t)(len+128);
3690 hdr.bastype = 3; // code
3691 hdr.baslength = (uint16_t)len;
3692 hdr.basaddr = (uint16_t)start;
3693 hdr.basvarsofs = 0;
3694 if (p3dskWriteFileHeader(&nfo, &hdr) != FLPERR_OK) { fprintf(stderr, "error writing +3DOS file header\n"); return -1; }
3697 return 0;
3701 static void saveDSK (const char *basename) {
3702 // create and format the floppy
3703 Floppy *flp = flpCreate(0);
3704 if (p3dskFormatDisk(flp, optDskType) != FLPERR_OK) {
3705 flpDestroy(flp);
3706 fprintf(stderr, "ERROR: cannot format +3DOS disk\n");
3707 return;
3710 P3DiskInfo p3d;
3711 p3d.flp = flp;
3712 if (p3dskDetectGeom(p3d.flp, &p3d.geom) < 0) {
3713 flpDestroy(flp);
3714 fprintf(stderr, "ERROR: not a +3DOS disk!\n");
3715 return;
3718 int hasbootsector = 0;
3719 for (OutDataFile *off = datafiles; off; off = off->next) {
3720 if (off->type == 1) { hasbootsector = 1; break; }
3723 int start = 0, len;
3724 int haschunks = findChunkFrom(start, &start, &len);
3726 if (!hasbootsector && haschunks) {
3727 if (saveDSKCargador(&p3d, optRunDSK) != 0) {
3728 flpDestroy(flp);
3729 return;
3733 // write chunks
3734 start = 0;
3735 while (findChunkFrom(start, &start, &len)) {
3736 char cname[16];
3737 snprintf(cname, sizeof(cname), "C%04X.BIN", (unsigned)start);
3738 if (saveDSKChunk(&p3d, cname, memory+(unsigned)start, start, len, 1) != 0) {
3739 flpDestroy(flp);
3740 return;
3742 start += len;
3745 // write data files
3746 for (OutDataFile *off = datafiles; off; off = off->next) {
3747 if (off->type == 1) {
3748 // +3DOS bootsector
3749 if (!p3d.geom.restracks) {
3750 fprintf(stderr, "no room for +3DOS bootsector\n");
3751 continue;
3753 uint8_t *bootsec = malloc(512);
3754 if (flpGetSectorData(p3d.flp, 0, p3d.geom.firstsector, bootsec, 512) != FLPERR_OK) {
3755 fprintf(stderr, "cannot read +3DOS bootsector\n");
3756 continue;
3758 size_t clen = off->size;
3759 if (clen > 512-16) clen = 512-16;
3760 if (clen) memcpy(bootsec+16, off->data, clen);
3761 if (flpPutSectorData(p3d.flp, 0, p3d.geom.firstsector, bootsec, 512) != FLPERR_OK) fatal("cannot write +3DOS bootsector");
3762 if (p3dskWriteBootableChecksum(&p3d) != FLPERR_OK) fatal("cannot make +3DOS disk bootable");
3763 printf("written +3DOS bootsector (%u bytes of code)\n", off->size);
3764 continue;
3767 if (off->type != 'C') {
3768 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3769 continue;
3771 char pdfname[13];
3772 if (p3dskNormaliseFileName(pdfname, off->name) != FLPERR_OK) {
3773 fprintf(stderr, "skipping data file with invalid name '%s'\n", off->name);
3774 continue;
3776 saveDSKChunk(&p3d, pdfname, off->data, off->start, off->size, off->hasstart);
3779 // write disk
3780 //char *fname = strprintf("%s/%s.dsk", optOutputDir, basename);
3781 char *fname = buildOutputFileName(basename, "dsk");
3782 FILE *fo = fopen(fname, "wb");
3783 if (!fo) { fprintf(stderr, "cannot create disk file '%s'\n", fname); free(fname); flpDestroy(flp); return; }
3784 if (dskSaveDSK(flp, fo) != FLPERR_OK) {
3785 unlink(fname);
3786 fprintf(stderr, "error writing disk file '%s'\n", fname);
3787 free(fname);
3788 flpDestroy(flp);
3789 return;
3791 fclose(fo);
3792 printf("out: %s\n", fname);
3793 free(fname);
3794 flpDestroy(flp);
3798 ///////////////////////////////////////////////////////////////////////////////
3799 // .dmb
3801 static int saveDMB (const char *fname) {
3802 char *fn;// = malloc(strlen(fname)+16);
3803 int start = 0, len;
3804 uint16_t pcnt = 0;
3805 FILE *fo;
3807 //fn = strprintf("%s/%s.dmb", optOutputDir, fname);
3808 fn = buildOutputFileName(fname, "dmb");
3809 fo = fopen(fn, "wb");
3810 free(fn);
3811 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
3813 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
3815 printf("out: %s.dmb\n", fname);
3816 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
3817 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
3818 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
3819 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
3821 start = 0;
3822 while (findChunkFrom(start, &start, &len)) {
3823 if (fWriteWord(fo, len, NULL) != 0) goto error;
3824 if (fWriteWord(fo, start, NULL) != 0) goto error;
3825 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
3826 start += len;
3828 fclose(fo);
3829 return 0;
3830 error:
3831 fclose(fo);
3832 unlink(fname);
3833 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
3834 return -1;
3838 ///////////////////////////////////////////////////////////////////////////////
3839 // .tap
3841 static char tapeLoaderName[16];
3844 static void saveTapCargador (FILE *fo) {
3845 // count blocks
3846 static uint8_t cargador[16384]; // should be enough for everyone
3847 int start = 0, len, pos, f;
3848 uint8_t bxor;
3851 void putStr (const char *s) {
3852 for (; *s; ++s) cargador[pos++] = *s;
3855 void putNum (int num) {
3856 char buf[64];
3857 sprintf(buf, "%d", num);
3858 putStr(buf);
3862 pos = 4;
3863 // number
3864 cargador[0] = 0; cargador[1] = 10;
3865 // size (will be fixed later)
3866 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
3867 // load blocks
3868 while (findChunkFrom(start, &start, &len)) {
3869 // :LOAD "" CODE
3870 putStr(":\xef\"\"\xaf");
3871 start += len;
3873 // additional code files
3874 for (OutDataFile *off = datafiles; off; off = off->next) {
3875 if (off->type == 1) continue; // skip +3DOS bootsector
3876 if (off->type != 'C' || off->size > 65533 || !off->hasstart) {
3877 continue;
3879 // :LOAD "" CODE
3880 putStr(":\xef\"\"\xaf");
3881 start += len;
3883 // and run
3884 // :RANDOMIZE USR VAL "xxx"
3885 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
3886 // patch len
3887 cargador[2] = (pos-4)&0xff;
3888 cargador[3] = ((pos-4)>>8)&0xff;
3889 // write header
3890 fWriteWord(fo, 19, NULL); // length of header
3891 bxor = 0;
3892 fWriteByte(fo, 0, &bxor); // header block
3893 fWriteByte(fo, 0, &bxor); // 'basic' flag
3894 if (tapeLoaderName[0]) {
3895 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3896 } else {
3897 fWriteByte(fo, 'c', &bxor);
3898 fWriteByte(fo, 'a', &bxor);
3899 fWriteByte(fo, 'r', &bxor);
3900 fWriteByte(fo, 'g', &bxor);
3901 fWriteByte(fo, 'a', &bxor);
3902 fWriteByte(fo, 'd', &bxor);
3903 fWriteByte(fo, 'o', &bxor);
3904 fWriteByte(fo, 'r', &bxor);
3905 fWriteByte(fo, ' ', &bxor);
3906 fWriteByte(fo, ' ', &bxor);
3908 fWriteWord(fo, pos, &bxor); // length
3909 fWriteWord(fo, 10, &bxor); // start
3910 fWriteWord(fo, pos, &bxor); // length2
3911 fWriteByte(fo, bxor, NULL);
3912 // write data
3913 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
3914 bxor = 0;
3915 fWriteByte(fo, 0xFFU, &bxor); // data block
3916 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
3917 fWriteByte(fo, bxor, NULL);
3921 static void saveTap (const char *basename) {
3922 char *fname;// = malloc(strlen(basename)+16);
3923 char blkname[128];
3924 int start = 0, len, f;
3925 uint8_t bxor;
3926 FILE *fo;
3928 //fname = strprintf("%s/%s.tap", optOutputDir, basename);
3929 fname = buildOutputFileName(basename, "tap");
3930 fo = fopen(fname, "wb");
3931 free(fname);
3932 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
3933 printf("out: %s.tap\n", basename);
3934 if (optRunTape) saveTapCargador(fo);
3935 while (findChunkFrom(start, &start, &len)) {
3936 // write header
3937 if (tapeLoaderName[0]) {
3938 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
3939 memcpy(blkname, tapeLoaderName, 10);
3940 } else {
3941 sprintf(blkname, "c%04X:%04X", start, len);
3943 //printf(" block: %s\n", blkname);
3944 fWriteWord(fo, 19, NULL); // length of header
3945 bxor = 0;
3946 fWriteByte(fo, 0, &bxor); // header block
3947 fWriteByte(fo, 3, &bxor); // 'code' flag
3948 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
3949 fWriteWord(fo, len, &bxor);
3950 fWriteWord(fo, start, &bxor);
3951 fWriteWord(fo, 32768, &bxor);
3952 fWriteByte(fo, bxor, NULL);
3953 // write data
3954 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
3955 bxor = 0;
3956 fWriteByte(fo, 0xFFU, &bxor); // data block
3957 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
3958 fWriteByte(fo, bxor, NULL);
3959 start += len;
3962 for (int dfpass = 0; dfpass < 2; ++dfpass) {
3963 // write data files
3964 for (OutDataFile *off = datafiles; off; off = off->next) {
3965 if (off->type == 1) continue; // skip +3DOS bootsector
3966 if (off->type != 'C' || off->size > 65533) {
3967 fprintf(stderr, "skipping data file with invalid type '%C'\n", off->type);
3968 continue;
3970 if (dfpass == 0) {
3971 if (!off->hasstart) continue;
3972 } else {
3973 if (off->hasstart) continue;
3975 snprintf(blkname, 10, "%s", off->name);
3976 blkname[10] = 0; // just in case
3977 while (strlen(blkname) < 10) strcat(blkname, " ");
3978 // write data block
3979 fWriteWord(fo, 19, NULL); // length of header
3980 bxor = 0;
3981 fWriteByte(fo, 0, &bxor); // header block
3982 fWriteByte(fo, 3, &bxor); // 'code' flag
3983 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
3984 fWriteWord(fo, off->size, &bxor);
3985 fWriteWord(fo, off->start, &bxor);
3986 fWriteWord(fo, 32768, &bxor);
3987 fWriteByte(fo, bxor, NULL);
3988 // write data
3989 fWriteWord(fo, off->size+2, NULL); // plus type and checkbyte
3990 bxor = 0;
3991 fWriteByte(fo, 0xFFU, &bxor); // data block
3992 for (f = 0; f < off->size; ++f) fWriteByte(fo, off->data[f], &bxor);
3993 fWriteByte(fo, bxor, NULL);
3997 fclose(fo);
4001 ///////////////////////////////////////////////////////////////////////////////
4002 // pseudoinstructions
4004 // note that processCurrentLine() will NOT skip to the next line before
4005 // calling pseudoinstruction handler!
4006 // note that processCurrentLine() will skip current line after calling
4007 // pseudoinstruction handler!
4009 static int wasOrg = 0;
4010 static int wasClr = 0;
4013 // ////////////////////////////////////////////////////////////////////////// //
4014 // print message using printf-like syntax
4015 // doesn't print new line
4016 static void processPrintf (FILE *fo, const char *fmt) {
4017 if (!fmt || !fmt[0]) return;
4018 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
4019 char *tempstr = NULL;
4020 size_t tempsize = 0;
4021 char *fmtcopy = malloc(strlen(fmt)+1);
4022 strcpy(fmtcopy, fmt);
4023 char *currfmt = fmtcopy;
4024 char tempbuf[512];
4025 int stlen;
4026 char *strarg;
4027 int defined;
4028 int32_t exprval;
4029 while (*currfmt) {
4030 /* find '%' */
4031 char *prcs = strchr(currfmt, '%');
4032 if (!prcs || !prcs[1]) {
4033 /* no more formatting; print the tail and exit the loop */
4034 fprintf(fo, "%s", currfmt);
4035 break;
4037 /* is this "%%"? */
4038 int docheck = 1;
4039 if (prcs[1] == '%') {
4040 ++prcs;
4041 docheck = 0;
4043 /* print up to `prcs` */
4044 if (prcs > currfmt) {
4045 size_t partlen = (ptrdiff_t)(prcs-currfmt);
4046 if (partlen+1 > tempsize) {
4047 tempsize = ((partlen+8)|0xff)+1;
4048 tempstr = realloc(tempstr, tempsize);
4050 memcpy(tempstr, currfmt, partlen);
4051 tempstr[partlen] = 0;
4052 fprintf(fo, "%s", tempstr);
4054 currfmt = ++prcs; /* skip percent */
4055 if (!docheck) continue;
4056 /* parse format */
4057 char sign = ' ';
4058 int width = 0;
4059 int zerofill = 0;
4060 char ftype = ' ';
4061 if (*currfmt == '+' || *currfmt == '-') sign = *currfmt++;
4062 if (sign != '-' && *currfmt == '0') zerofill = 1;
4063 while (isDigit(*currfmt)) { width = width*10+((*currfmt)-'0'); ++currfmt; }
4064 if (width > 256) width = 256;
4065 ftype = *currfmt++;
4066 if (!ftype) break; /* oops */
4067 if (!eatComma()) fatal("out of arguments for string format");
4068 switch (ftype) {
4069 case 's': /* string */
4070 stlen = 0;
4071 switch (strSkipSpaces(currLine)[0]) {
4072 case '"': case '\'': /* string literal? */
4073 strarg = getStrArg(&stlen);
4074 break;
4075 default: /* expression */
4076 strarg = getStrExprArgFmt();
4077 stlen = (int)strlen(strarg);
4078 break;
4080 /* left pad */
4081 if (sign != '-' && stlen < width) {
4082 int padlen = width-stlen;
4083 memset(tempbuf, ' ', padlen);
4084 tempbuf[padlen+1] = 0;
4085 fprintf(fo, "%s", tempbuf);
4087 fprintf(fo, "%s", strarg);
4088 /* right pad */
4089 if (sign == '-' && stlen < width) {
4090 int padlen = width-stlen;
4091 memset(tempbuf, ' ', padlen);
4092 tempbuf[padlen+1] = 0;
4093 fprintf(fo, "%s", tempbuf);
4095 break;
4096 case 'd': /* decimal */
4097 defined = 1;
4098 exprval = getExprArg(&defined, NULL);
4099 if (width && zerofill) {
4100 fprintf(fo, "%0*d", width, exprval);
4101 } else if (width) {
4102 fprintf(fo, "%*d", (sign != '-' ? width : -width), exprval);
4103 } else {
4104 fprintf(fo, "%d", exprval);
4106 break;
4107 case 'c': /* char */
4108 defined = 1;
4109 exprval = getExprArg(&defined, NULL);
4110 if (exprval <= 0 || exprval == '?' || exprval > 255) exprval = '?';
4111 if (width) {
4112 fprintf(fo, "%*c", (sign != '-' ? width : -width), exprval);
4113 } else {
4114 fprintf(fo, "%c", exprval);
4116 break;
4117 case 'u': /* unsigned */
4118 defined = 1;
4119 exprval = getExprArg(&defined, NULL);
4120 if (width && zerofill) {
4121 fprintf(fo, "%0*u", width, (unsigned)(exprval&0xffff));
4122 } else if (width) {
4123 fprintf(fo, "%*u", (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
4124 } else {
4125 fprintf(fo, "%u", (unsigned)(exprval&0xffff));
4127 break;
4128 case 'x': case 'X': /* hex */
4129 defined = 1;
4130 exprval = getExprArg(&defined, NULL);
4131 if (width && zerofill) {
4132 fprintf(fo, (ftype == 'x' ? "%0*x" : "%0*X"), width, (unsigned)(exprval&0xffff));
4133 } else if (width) {
4134 fprintf(fo, (ftype == 'x' ? "%*x" : "%*X"), (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
4135 } else {
4136 fprintf(fo, (ftype == 'x' ? "%x" : "%X"), (unsigned)(exprval&0xffff));
4138 break;
4139 default:
4140 if (ftype <= 0 || ftype == 127) ftype = '?';
4141 fatal("invalid format specifier: '%c'", ftype);
4142 break;
4145 if (tempstr) free(tempstr);
4146 free(fmtcopy);
4150 ///////////////////////////////////////////////////////////////////////////////
4151 // user error
4153 static int piERROR (void) {
4154 int len = 0;
4155 char *res = getStrArg(&len);
4156 fprintf(stdout, "*** USER ERROR: ");
4157 processPrintf(stdout, res);
4158 fputc('\n', stdout);
4159 fatal("user error abort");
4160 return PI_SKIP_LINE;
4164 static int piWARNING (void) {
4165 int len = 0;
4166 char *res = getStrArg(&len);
4167 fprintf(stdout, "*** USER WARNING ");
4168 if (currSrcLine) {
4169 fprintf(stdout, "at file %s, line %d: ", currSrcLine->fname, currSrcLine->lineNo);
4170 } else {
4171 fprintf(stdout, "somewhere in time: ");
4173 processPrintf(stdout, res);
4174 fputc('\n', stdout);
4175 return PI_SKIP_LINE;
4179 ///////////////////////////////////////////////////////////////////////////////
4180 // user warnings
4182 static int piPrintfCommon (int passNo) {
4183 if (passNo < 0 || pass == passNo) {
4184 int len = 0;
4185 char *res = getStrArg(&len);
4186 processPrintf(stdout, res);
4187 fputc('\n', stdout);
4189 return PI_SKIP_LINE;
4193 static int piDISPLAYX (int passNo, int asHex) {
4194 for (;;) {
4195 if (isStrArg()) {
4196 int len = 0;
4197 char *res = getStrArg(&len);
4199 if (passNo < 0 || pass == passNo) printf("%s", res);
4200 } else {
4201 int defined = 1;
4202 int32_t v = getExprArg(&defined, NULL);
4204 if (passNo < 0 || pass == passNo) {
4205 if (asHex) printf("%04X", (unsigned int)v);
4206 else printf("%d", v);
4209 if (!eatComma()) break;
4211 return PI_SKIP_LINE;
4215 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
4216 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
4217 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
4218 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
4219 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
4220 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
4222 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
4223 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
4224 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
4227 ///////////////////////////////////////////////////////////////////////////////
4228 // ORG, DISP, etc.
4230 static int piORG (void) {
4231 int defined = 1;
4232 int32_t res = getOneExprArg(&defined, NULL);
4234 if (!defined) fatal("sorry, ORG operand value must be known here");
4235 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
4236 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
4237 pc = disp = res;
4238 if (!wasOrg) {
4239 wasOrg = 1;
4240 ent = res;
4241 if (!wasClr && res > 0) clrAddr = res-1;
4243 return PI_CONT_LINE;
4247 static int piDISP (void) {
4248 int defined = 1;
4249 int32_t res = getOneExprArg(&defined, NULL);
4251 if (!defined) fatal("sorry, DISP operand value must be known here");
4252 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
4253 //printf("DISP=%d\n", res);
4254 disp = res;
4255 return PI_CONT_LINE;
4259 static int piENDDISP (void) {
4260 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
4261 checkExprEnd();
4262 disp = pc;
4263 return PI_CONT_LINE;
4267 static int piENT (void) {
4268 int defined = 1;
4269 int32_t res = getOneExprArg(&defined, NULL);
4271 //if (!defined) fatal("sorry, ENT operand value must be known here");
4272 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
4273 ent = res;
4274 return PI_CONT_LINE;
4278 static int piCLR (void) {
4279 int defined = 1;
4280 int32_t res = getOneExprArg(&defined, NULL);
4282 //if (!defined) fatal("sorry, CLR operand value must be known here");
4283 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
4284 clrAddr = res;
4285 wasClr = 1;
4286 return PI_CONT_LINE;
4290 // RESERVE start, count
4291 static int piRESERVE (void) {
4292 int defined = 1, start;
4293 int32_t res = getExprArg(&defined, NULL);
4294 if (!defined) fatal("sorry, RESERVE operand values must be known here");
4295 if (res < 0 || res > 65535) fatal("invalid RESERVE address value: %d", res);
4296 start = res;
4297 if (!eatComma()) fatal("RESERVE needs 2 args");
4298 res = getOneExprArg(&defined, NULL);
4299 if (!defined) fatal("sorry, RESERVE operand values must be known here");
4300 if (res < 0 || res > 65535) fatal("invalid RESERVE length value: %d", res);
4301 while (res--) {
4302 if (memused[start]) fatal("trying to reserve already used memory at #%04X", start);
4303 memresv[start++] = 1;
4305 return PI_CONT_LINE;
4309 static int piALIGN (void) {
4310 int defined = 1;
4311 int32_t res;
4313 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
4314 res = getOneExprArg(&defined, NULL);
4315 if (!defined) fatal("sorry, ALIGN operand value must be known here");
4316 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
4317 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
4318 if (res > 0 && pc%res != 0) {
4319 pc /= res;
4320 pc *= res;
4321 pc += res;
4322 disp = pc;
4324 return PI_CONT_LINE;
4328 static int piDISPALIGN (void) {
4329 int defined = 1;
4330 int32_t res = getOneExprArg(&defined, NULL);
4332 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
4333 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
4334 if (res > 0 && disp%res != 0) {
4335 disp /= res;
4336 disp *= res;
4337 disp += res;
4339 return PI_CONT_LINE;
4343 ///////////////////////////////////////////////////////////////////////////////
4344 // DEFx
4346 // DEFINCR operations
4347 enum {
4348 DFI_SHR,
4349 DFI_SHL,
4350 DFI_SAR, // arith shift, extend bit 7
4351 DFI_SAL, // arith shift, extend bit 0
4352 DFI_ROR,
4353 DFI_ROL,
4354 DFI_AND,
4355 DFI_XOR,
4356 DFI_OR,
4357 DFI_ADD,
4358 DFI_SUB,
4359 DFI_MUL,
4360 DFI_DIV,
4361 DFI_MOD,
4364 typedef struct DefIncrOp_s DefIncrOp;
4365 struct DefIncrOp_s {
4366 DefIncrOp *next;
4367 uint16_t operator; // DFI_xxx
4368 uint16_t operand;
4371 static DefIncrOp *dfi_list = NULL;
4372 //static int defIncr = 0;
4375 static void dfi_free (void) {
4376 while (dfi_list) {
4377 DefIncrOp *c = dfi_list;
4378 dfi_list = c->next;
4379 free(c);
4384 //FIXME: make this faster
4385 #define DFI_XSHIFT(sop_) do { \
4386 for (uint16_t f = 0; f < opnd; ++f) { \
4387 val = ((val&xmask) sop_ 1)|(val&andmask ? ormask : 0); \
4389 } while (0)
4392 static int32_t dfi_apply (int32_t val, int isbyte) {
4393 const uint16_t xmask = (isbyte ? 0xff : 0xffff);
4394 val &= xmask;
4395 uint16_t andmask, ormask;
4396 for (DefIncrOp *dop = dfi_list; dop; dop = dop->next) {
4397 uint16_t opnd = dop->operand;
4398 switch (dop->operator) {
4399 case DFI_SHR:
4400 if (!opnd) break;
4401 if (opnd > (isbyte ? 7 : 15)) { val = 0; break; }
4402 val = (val&xmask)>>opnd;
4403 break;
4404 case DFI_SHL:
4405 if (!opnd) break;
4406 if (opnd > (isbyte ? 7 : 15)) { val = 0; break; }
4407 val = (val&xmask)<<opnd;
4408 break;
4409 case DFI_SAR:
4410 if (!opnd) break;
4411 if (isbyte) {
4412 if (opnd > 7) { val = (val&0x80 ? 0xff : 0x00); break; }
4413 andmask = ormask = 0x80;
4414 } else {
4415 if (opnd > 15) { val = (val&0x8000 ? 0xffff : 0x0000); break; }
4416 andmask = ormask = 0x8000;
4418 DFI_XSHIFT(>>);
4419 break;
4420 case DFI_SAL:
4421 if (!opnd) break;
4422 if (opnd > (isbyte ? 7 : 15)) { val = (val&0x01 ? 0xff : 0x00); break; }
4423 andmask = ormask = 0x01;
4424 DFI_XSHIFT(<<);
4425 break;
4426 case DFI_ROR:
4427 opnd &= (isbyte ? 7 : 15);
4428 if (!opnd) break;
4429 andmask = 0x01;
4430 ormask = (isbyte ? 0x80 : 0x8000);
4431 DFI_XSHIFT(>>);
4432 break;
4433 case DFI_ROL:
4434 opnd &= (isbyte ? 7 : 15);
4435 if (!opnd) break;
4436 andmask = (isbyte ? 0x80 : 0x8000);
4437 ormask = 0x01;
4438 DFI_XSHIFT(<<);
4439 break;
4440 case DFI_AND:
4441 val &= opnd;
4442 break;
4443 case DFI_XOR:
4444 val ^= opnd;
4445 break;
4446 case DFI_OR:
4447 val |= opnd;
4448 break;
4449 case DFI_ADD:
4450 val += opnd;
4451 break;
4452 case DFI_SUB:
4453 val -= opnd;
4454 break;
4455 case DFI_MUL:
4456 val *= opnd;
4457 break;
4458 case DFI_DIV:
4459 if (opnd) val /= opnd; else val = 0;
4460 break;
4461 case DFI_MOD:
4462 if (opnd) val %= opnd; else val = 0;
4463 break;
4465 val &= xmask;
4467 return val;
4471 static int piDEFINCR (void) {
4472 #if 0
4473 int defined = 1;
4474 int32_t cnt = getOneExprArg(&defined, NULL);
4475 if (!defined) fatal("DEFINCR: increment must be defined");
4476 defIncr = cnt;
4477 return PI_CONT_LINE;
4478 #else
4479 dfi_free();
4480 if (isStrArg()) {
4481 int len = 0;
4482 char *rstr = getStrArg(&len);
4483 if (!rstr || !strEquCI(rstr, "expr")) fatal("invalid DEFINCR command (\"expr\" expected)");
4484 DefIncrOp *last = NULL;
4485 while (eatComma()) {
4486 len = 0;
4487 rstr = getStrArg(&len);
4488 if (!rstr || len != 3) fatal("invalid DEFINCR operator");
4489 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4490 if (strEquCI(rstr, "SHR")) dop->operator = DFI_SHR;
4491 else if (strEquCI(rstr, "SHL")) dop->operator = DFI_SHL;
4492 else if (strEquCI(rstr, "SAR")) dop->operator = DFI_SAR;
4493 else if (strEquCI(rstr, "SAL")) dop->operator = DFI_SAL;
4494 else if (strEquCI(rstr, "ROR")) dop->operator = DFI_ROR;
4495 else if (strEquCI(rstr, "ROL")) dop->operator = DFI_ROL;
4496 else if (strEquCI(rstr, "AND")) dop->operator = DFI_AND;
4497 else if (strEquCI(rstr, "XOR")) dop->operator = DFI_XOR;
4498 else if (strEquCI(rstr, "OR")) dop->operator = DFI_OR;
4499 else if (strEquCI(rstr, "ADD")) dop->operator = DFI_ADD;
4500 else if (strEquCI(rstr, "SUB")) dop->operator = DFI_SUB;
4501 else if (strEquCI(rstr, "MUL")) dop->operator = DFI_MUL;
4502 else if (strEquCI(rstr, "DIV")) dop->operator = DFI_DIV;
4503 else if (strEquCI(rstr, "MOD")) dop->operator = DFI_MOD;
4504 else fatal("invalid DEFINCR operator '%s'", rstr);
4505 dop->next = NULL;
4506 if (!eatComma()) fatal("DEFINCR: operand expected");
4507 int defined = 1;
4508 int32_t res = getExprArg(&defined, NULL);
4509 if (!defined) fatal("DEFINCR: operand must be defined");
4510 if (res < 0) fatal("DEFINCR: operand must be positive");
4511 res &= 0xffff;
4512 dop->operand = (uint16_t)res;
4513 if (last) last->next = dop; else dfi_list = dop;
4514 last = dop;
4516 } else {
4517 int defined = 1;
4518 int32_t cnt = getOneExprArg(&defined, NULL);
4519 if (!defined) fatal("DEFINCR: increment must be defined");
4520 //defIncr = cnt;
4521 if (cnt > 0) {
4522 cnt &= 0xffff;
4523 if (cnt) {
4524 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4525 dop->next = NULL;
4526 dop->operator = DFI_ADD;
4527 dop->operand = cnt;
4528 dfi_list = dop;
4530 } else {
4531 cnt &= 0xffff; //UB, i don't care
4532 if (cnt) {
4533 DefIncrOp *dop = malloc(sizeof(DefIncrOp));
4534 dop->next = NULL;
4535 dop->operator = DFI_SUB;
4536 dop->operand = cnt;
4537 dfi_list = dop;
4541 #endif
4542 return PI_CONT_LINE;
4546 static int piDEFBW (int isWord) {
4547 int32_t res;
4548 for (;;) {
4549 int defined = 0, fixuptype = UR_FIXUP_NONE;
4551 if (isStrArg()) {
4552 int f, len = 0;
4553 char *rstr = getStrExprArg(&len, isWord);
4554 if (rstr) {
4555 for (f = 0; f < len; ++f) {
4556 int32_t b = (uint8_t)rstr[f];
4557 //b += defIncr;
4558 b = dfi_apply(b, 1/*isbyte*/);
4559 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
4560 if (b < 0) b += 256;
4561 emitByte(b);
4563 if (!eatComma()) break;
4564 continue;
4565 } else {
4566 defined = 1;
4567 res = len;
4569 } else {
4570 res = getExprArg(&defined, &fixuptype);
4574 //int32_t res = getExprArg(&defined, &fixuptype);
4576 if (pass > 0 && !defined) fatal("undefined operand");
4577 //res += defIncr;
4578 res = dfi_apply(res, !isWord/*isbyte*/);
4579 if (isWord) {
4580 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
4581 if (res < 0) res += 65536;
4582 if (isWord == 1) {
4583 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 2);
4584 emitWord(res);
4585 } else {
4586 /* reversed word */
4587 switch (fixuptype) {
4588 case UR_FIXUP_WORD: /* swapped bytes */
4589 urasm_fixup_operand(NULL, pc, disp, UR_FIXUP_HIBYTE, 1);
4590 urasm_fixup_operand(NULL, pc, disp+1, UR_FIXUP_LOBYTE, 1);
4591 break;
4592 case UR_FIXUP_LOBYTE:
4593 case UR_FIXUP_HIBYTE:
4594 warningMsg("non-word fixup for reversed word; wtf?!");
4595 break;
4596 default: break;
4598 emitRWord(res);
4600 } else {
4601 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
4602 if (fixuptype != UR_FIXUP_NONE) {
4603 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
4604 urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
4606 if (res < 0) res += 256;
4607 emitByte(res);
4610 if (!eatComma()) break;
4612 return PI_CONT_LINE;
4615 static int piDEFB (void) { return piDEFBW(0); }
4616 static int piDEFW (void) { return piDEFBW(1); }
4617 static int piDEFR (void) { return piDEFBW(2); }
4620 static int piDEFS (void) {
4621 for (;;) {
4622 int32_t bt, f;
4623 int defined = 0, fixuptype = UR_FIXUP_NONE;
4624 int32_t res = getExprArg(&defined, &fixuptype);
4626 if (pass > 0 && !defined) fatal("undefined operand");
4627 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
4628 if (*currLine && currLine[0] == ',') {
4629 (void)eatComma();
4630 bt = getExprArg(&defined, NULL);
4631 if (pass > 0 && !defined) fatal("undefined operand");
4632 //bt += defIncr;
4633 bt = dfi_apply(bt, 1/*isbyte*/);
4634 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
4635 if (bt < 0) bt += 256;
4636 if (fixuptype != UR_FIXUP_NONE) {
4637 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
4639 for (f = 0; f < res; ++f) {
4640 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
4641 emitByte(bt);
4643 } else {
4644 pc += res; disp += res;
4646 if (!eatComma()) break;
4648 return PI_CONT_LINE;
4652 /* bit 0: put '\0' */
4653 /* bit 1: set bit 7 of last byte */
4654 /* bit 2: put length */
4655 static int piDEFSTR (int type) {
4656 for (;;) {
4657 if (isStrArg()) {
4658 int f, len = 0;
4659 char *res = getStrArg(&len);
4661 if (type&0x04) {
4662 if (len > 255) fatal("string too long");
4663 emitByte(len);
4665 for (f = 0; f < len; ++f) {
4666 uint8_t b = (uint8_t)res[f];
4668 if ((type&0x02) && f == len-1) b |= 0x80;
4669 emitByte(b);
4671 if (type&0x01) emitByte(0);
4672 } else {
4673 int defined = 1;
4674 int32_t v = getExprArg(&defined, NULL);
4676 if (pass > 0 && !defined) fatal("undefined expression");
4677 if (!defined) v = 0; else v = dfi_apply(v, 1/*isbyte*/);
4678 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
4679 if (v < 0) v += 256;
4680 emitByte(v);
4682 if (!eatComma()) break;
4684 return PI_CONT_LINE;
4688 static int piDEFM (void) { return piDEFSTR(0x00); }
4689 static int piDEFZ (void) { return piDEFSTR(0x01); }
4690 static int piDEFX (void) { return piDEFSTR(0x02); }
4691 static int piDEFC (void) { return piDEFSTR(0x04); }
4694 ///////////////////////////////////////////////////////////////////////////////
4695 // INCBIN
4697 /* INCBIN "name"[,maxlen[,offset]] */
4698 static int piINCBIN (void) {
4699 int system = 0;
4700 char *fn, qCh;
4701 uint8_t bt;
4702 FILE *fl;
4703 int maxlen = 65536;
4704 int offset = 0;
4705 char *args = currLine;
4707 if (!currLine[0]) fatal("INCBIN without file name");
4709 if (isStrArg()) {
4710 qCh = *args++;
4711 system = 0;
4712 } else if (currLine[0] == '<') {
4713 qCh = '>'; ++args;
4714 system = 1;
4715 } else {
4716 qCh = 0;
4717 system = 0;
4720 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4721 int softinclude = 0;
4722 if (fn[0] == '?') { softinclude = 1; ++fn; while (isSpace(*fn)) ++fn; }
4723 if (!fn[0]) fatal("INCBIN: empty file name");
4724 // now fix the name
4725 char *fname = createIncludeName(fn, system, NULL, NULL);
4726 memmove(currLine, args, strlen(args)+1);
4728 // maxlen
4729 if (currLine[0] == ',') {
4730 int defined = 1;
4731 (void)eatComma();
4732 maxlen = getExprArg(&defined, NULL);
4733 if (!defined) fatal("INCBIN: undefined maxlen");
4734 if (maxlen < 1) { free(fname); return 1; } // nothing to do
4737 // offset (negative: from the end)
4738 if (currLine[0] == ',') {
4739 int defined = 1;
4740 (void)eatComma();
4741 offset = getOneExprArg(&defined, NULL);
4742 if (!defined) fatal("INCBIN: undefined offset");
4743 } else {
4744 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
4747 fl = fopen(fname, "rb");
4748 if (!fl) {
4749 if (!softinclude) fatal("INCBIN: file not found: '%s'", fname);
4750 } else {
4751 if (offset) {
4752 if (fseek(fl, offset, (offset < 0 ? SEEK_END : SEEK_SET)) < 0) {
4753 fclose(fl);
4754 fatal("INCBIN: error seeking to %d in file '%s'", offset, fname);
4757 while (maxlen-- > 0) {
4758 int res = fread(&bt, 1, 1, fl);
4759 if (!res) break;
4760 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: '%s'", fname); }
4761 emitByte(bt);
4763 fclose(fl);
4766 free(fname);
4767 return PI_SKIP_LINE;
4771 /* DATABIN "name"[,len[,offset[,codeaddr]]] */
4772 /* DATABIN "name|dfname"[,len[,offset[,codeaddr]]] */
4773 static int piDATABINcommon (int ascode) {
4774 if (pass != 1) return PI_SKIP_LINE;
4776 int system = 0;
4777 char *fn, qCh;
4778 int maxlen = -1;
4779 int offset = 0;
4780 int codeaddr = 32768;
4781 int hasstart = 0;
4782 char *args = currLine;
4784 if (!currLine[0]) fatal("DATABIN without file name");
4786 if (isStrArg()) {
4787 qCh = *args++;
4788 system = 0;
4789 } else if (currLine[0] == '<') {
4790 qCh = '>'; ++args;
4791 system = 1;
4792 } else {
4793 qCh = 0;
4794 system = 0;
4797 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4798 int allowskip = 0;
4799 if (fn[0] == '?') { allowskip = 1; ++fn; while (isSpace(*fn)) ++fn; }
4800 if (!fn[0]) fatal("DATABIN: empty file name");
4801 char *fnameup = strdup(fn);
4802 memmove(currLine, args, strlen(args)+1);
4804 // maxlen
4805 if (currLine[0] == ',') {
4806 int defined = 1;
4807 (void)eatComma();
4808 maxlen = getExprArg(&defined, NULL);
4809 if (!defined) fatal("DATABIN: undefined length");
4810 if (maxlen < 1) { free(fnameup); return 1; } // nothing to do
4813 // offset (negative: from the end)
4814 if (currLine[0] == ',') {
4815 int defined = 1;
4816 (void)eatComma();
4817 offset = getExprArg(&defined, NULL);
4818 if (!defined) fatal("DATABIN: undefined offset");
4821 // code address
4822 if (currLine[0] == ',') {
4823 int defined = 1;
4824 (void)eatComma();
4825 codeaddr = getOneExprArg(&defined, NULL);
4826 if (!defined) fatal("DATABIN: undefined codeaddr");
4827 hasstart = 1;
4828 } else {
4829 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
4832 // find and extract "disk name"
4833 char *diskname;
4835 char *pipeptr = strchr(fnameup, '|');
4836 if (pipeptr) {
4837 if (!pipeptr[1]) fatal("empty data file output name");
4838 diskname = strdup(pipeptr+1);
4839 *pipeptr = 0;
4840 } else {
4841 // build output name from disk name
4842 char *slp = strrchr(fnameup, '/');
4843 #ifdef WIN32
4844 char *slp1 = strrchr(fnameup, '\\');
4845 if (slp1 && (!slp || slp1 > slp)) slp = slp1;
4846 #endif
4847 if (slp) {
4848 ++slp;
4849 if (!slp[0]) fatal("empty data file output name");
4850 diskname = strdup(slp);
4851 } else {
4852 diskname = strdup(fnameup);
4856 // now fix the name
4857 char *fname = createIncludeName(fnameup, system, NULL, NULL);
4858 free(fnameup);
4860 OutDataFile *off = appendDataFile(fname, offset, maxlen, allowskip);
4862 if (off) {
4863 off->type = 'C';
4864 off->name = diskname;
4865 off->start = codeaddr&0xffffU;
4866 off->hasstart = (hasstart || ascode);
4867 //for (char *s = off->name; *s; ++s) *s = toUpper(*s);
4868 printf("data file: %s (start=%u; size=%u; std=%d)\n", off->name, off->start, off->size, off->hasstart);
4871 free(fname);
4872 return PI_SKIP_LINE;
4876 static int piDATABIN (void) { return piDATABINcommon(0); }
4877 static int piCODEBIN (void) { return piDATABINcommon(1); }
4880 ///////////////////////////////////////////////////////////////////////////////
4881 // INCLUDE
4883 /* INCLUDE "name" */
4884 static int piINCLUDE (void) {
4885 int system = 0;
4886 char *fn, qCh;
4887 char *args = currLine;
4888 if (!currLine[0]) fatal("INCLUDE without file name");
4889 if (isStrArg()) {
4890 qCh = *args++;
4891 system = 0;
4892 } else if (currLine[0] == '<') {
4893 qCh = '>'; ++args;
4894 system = 1;
4895 } else {
4896 qCh = 0;
4897 system = 0;
4899 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4900 // flags
4901 int softinclude = 0;
4902 if (fn[0] == '?') { softinclude = 1; ++fn; while (isSpace(*fn)) ++fn; }
4903 if (!fn[0]) fatal("INCLUDE: empty file name");
4904 // load
4905 if (asmTextInclude(fn, system, softinclude) != 0) {
4906 if (!softinclude) fatal("INCLUDE: some shit happens!");
4908 return PI_SKIP_LINE;
4912 ///////////////////////////////////////////////////////////////////////////////
4913 // MODULE, ENDMODULE
4915 static int piENDMODULE (void) {
4916 if (!currModule) fatal("ENDMODULE without MODULE");
4917 const char *mn = NULL;
4918 if (currLine[0]) mn = getOneLabelArg();
4919 moduleClose(mn);
4920 return PI_SKIP_LINE;
4924 static int piMODULE (void) {
4925 ModuleInfo *mi;
4926 char *mn;
4927 SourceLine *ol = currSrcLine;
4928 int inum;
4929 if (currModule) fatal("no nested modules allowed");
4930 mn = getOneLabelArg();
4931 if (!urasm_is_valid_name(mn)) fatal("invalid module name: %s", mn);
4932 mi = moduleFind(mn);
4933 //fprintf(stderr, "+++MODULE: [%s] %p (seen=%d)\n", mn, mi, (mi ? mi->seen : -1));
4934 if (mi) {
4935 if (mi->seen) {
4936 if (strcmp(mi->fname, currSrcLine->fname)) {
4937 fatal("duplicate module definition; previous was in %s", mi->fname);
4939 /* skip module */
4940 nextSrcLine(); /* skip "MODULE" line */
4941 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
4942 setCurSrcLine(ol);
4943 fatal("no ENDMODULE");
4945 if (inum == 0) fatal("no nested modules allowed");
4946 currModule = mi;
4947 moduleOpen();
4948 //skipInstruction(); //k8:wtf?!
4949 return piENDMODULE();
4951 } else {
4952 mi = moduleAdd(mn, currSrcLine->fname);
4954 mi->seen = 1;
4955 currModule = mi;
4956 moduleOpen();
4957 return PI_SKIP_LINE;
4961 /* Z80, Z80N */
4962 static int piMODEL (void) {
4963 char *mn = getOneIdArgLo();
4964 if (strSkipSpaces(currLine)[0]) fatal("only one model name expected");
4965 if (strcmp(mn, "z80") == 0) urasm_allow_zxnext = 0;
4966 else if (strcmp(mn, "z80a") == 0) urasm_allow_zxnext = 0;
4967 else if (strcmp(mn, "z80n") == 0) urasm_allow_zxnext = 1;
4968 else if (strcmp(mn, "z80next") == 0) urasm_allow_zxnext = 1;
4969 else if (strcmp(mn, "zxnext") == 0) urasm_allow_zxnext = 1;
4970 else fatal("invalid model name: %s", mn);
4971 return PI_SKIP_LINE;
4975 ////////////////////////////////////////////////////////////////////////////////
4976 // $SAVECODE "filename",addr,size [,ldaddr [,options...]]
4978 // options:
4979 // wipe -- wipe saved area (mark it as unused)
4981 static int piSAVECODE (void) {
4982 //if (pass != 1) return PI_SKIP_LINE;
4983 //char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
4984 //matchDelim(',');
4986 int optWipe = 0, optData = 0;
4987 char *fn, qCh;
4988 char *args = currLine;
4990 if (!currLine[0]) fatal("SAVECODE without file name");
4992 if (isStrArg()) {
4993 qCh = *args++;
4994 } else if (currLine[0] == '<') {
4995 qCh = '>'; ++args;
4996 } else {
4997 qCh = 0;
5000 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
5001 if (!fn[0]) fatal("DATABIN: empty file name");
5003 char *fname = strdup(fn);
5004 memmove(currLine, args, strlen(args)+1);
5006 // addr
5007 matchDelim(',');
5008 int defined = 1;
5009 int staddr = getExprArg(&defined, NULL);
5010 if (!defined) fatal("SAVECODE: undefined start address");
5011 if (staddr < 0 || staddr > 65535) fatal("SAVECODE: invalid start address (%d)", staddr);
5013 // size
5014 matchDelim(',');
5015 defined = 1;
5016 int size = getExprArg(&defined, NULL);
5017 if (!defined) fatal("SAVECODE: undefined size");
5018 if (size < 0 || size > 65535 || staddr+size > 65536) fatal("SAVECODE: invalid size");
5020 // load address
5021 int ldaddr = staddr;
5022 if (checkDelim(',')) {
5023 defined = 1;
5024 ldaddr = getExprArg(&defined, NULL);
5025 if (!defined) fatal("SAVECODE: undefined load address");
5026 if (ldaddr < 0 || ldaddr > 65535) fatal("SAVECODE: invalid load address");
5029 // parse options
5030 while (checkDelim(',')) {
5031 char *nstr = getLabelArg(0/*checkdelim*/);
5032 if (strEquCI(nstr, "wipe")) { optWipe = 1; continue; }
5033 if (strEquCI(nstr, "data")) { optData = 1; continue; }
5034 fatal("SAVECODE: unknown option '%s'", nstr);
5036 if (!isLineEnd()) fatal("SAVECODE: unknown extra args");
5038 // save, if we are on the second pass
5039 if (pass == 1 && size > 0) {
5040 OutDataFile *off = appendDataData(memory+(unsigned)staddr, (unsigned)staddr, (unsigned)size);
5041 if (!off) fatal("SAVECODE: out of memory");
5042 off->name = fname;
5043 off->hasstart = !optData;
5044 fname = NULL;
5047 if (optWipe) memset(memused+staddr, 0, size);
5049 if (fname) free(fname);
5050 return PI_SKIP_LINE;
5054 ////////////////////////////////////////////////////////////////////////////////
5055 // STRUCT
5057 static int piENDS (void) {
5058 fatal("ENDS without STRUCT");
5059 checkOperatorEnd();
5060 return PI_SKIP_LINE;
5064 // this will be registered for each new struct
5065 // use `urCurrentOp` to check what structure we are creating
5066 // `currSeenLabel` contains current seen label
5067 static int piSTRUCT_Internal (void) {
5068 StructInfo *sth = urCurrentOp->udata;
5070 // reserve room, set default values, add labels
5071 uint16_t origPC = pc;
5072 uint16_t origDisp = disp;
5074 pc += sth->size;
5075 disp += sth->size;
5077 if (sth->zerofill) {
5078 for (uint16_t f = 0; f < sth->size; ++f) {
5079 putByte(origPC+f, 0);
5083 for (StructField *fld = sth->fields; fld; fld = fld->next) {
5084 if (fld->hasInit) {
5085 if (fld->size == 1) {
5086 const uint8_t v = (fld->initValue < 0 ? 0x100+fld->initValue : fld->initValue)&0xff;
5087 putByte(origPC+fld->ofs, v);
5088 } else if (fld->size == 2) {
5089 const uint16_t v = (fld->initValue < 0 ? 0x10000+fld->initValue : fld->initValue)&0xffff;
5090 putWord(origPC+fld->ofs, v);
5091 } else if (fld->size == 4) {
5092 uint32_t v = 0;
5093 memcpy(&v, &fld->initValue, 4);
5094 putWord(origPC+fld->ofs, v&0xffff);
5095 putWord(origPC+fld->ofs+2, (v>>16)&0xffff);
5096 } else {
5097 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth->name);
5101 if (pass) continue;
5103 //fprintf(stderr, "LBL: <%s>\n", currSeenLabel);
5104 // need to create a new label?
5105 if (!currSeenLabel[0] || !fld->name || !fld->name[0]) continue;
5107 char *nn = mangleLabelName(currSeenLabel);
5108 char *lbn = malloc(strlen(nn)+strlen(fld->name)+64);
5109 sprintf(lbn, "%s.%s", nn, fld->name);
5111 UrLabelInfo *c = urFindLabel(lbn);
5112 if (!c) c = urAddLabel(lbn);
5113 free(lbn);
5115 c->type = LBL_TYPE_STOFS;
5116 c->value = origDisp+fld->ofs;
5117 c->known = 1;
5120 // create sizeof?
5121 if (currSeenLabel[0]) {
5122 char *nn = mangleLabelName(currSeenLabel);
5123 char *lbn = malloc(strlen(nn)+strlen("_sizeof")+64);
5124 sprintf(lbn, "%s.%s", nn, "_sizeof");
5126 UrLabelInfo *c = urFindLabel(lbn);
5127 if (!c) c = urAddLabel(lbn);
5128 free(lbn);
5130 c->type = LBL_TYPE_STOFS;
5131 c->value = sth->size;
5132 c->known = 1;
5135 // now parse initial values, if there are any
5136 if (!isLineEnd()) {
5137 const int isCurly = checkDelim('{');
5138 SourceLine *stline = currSrcLine;
5140 int wasNewLine = 1; // don't require a comma
5141 for (;;) {
5142 if (!currSrcLine) {
5143 if (isCurly) {
5144 setCurSrcLine(stline);
5145 fatal("STRUCT: no closing curly bracket");
5147 break;
5149 if (isLineEnd()) {
5150 if (!isCurly) break;
5151 // load next line
5152 nextSrcLine();
5153 wasNewLine = 1;
5154 continue;
5157 if (isCurly && checkDelim('}')) {
5158 // we're done
5159 if (!isLineEnd()) fatal("STRUCT: closing curly bracket must be alone on the line");
5160 break;
5163 if (wasNewLine) {
5164 wasNewLine = 0;
5165 } else {
5166 matchDelim(',');
5169 char *nstr = getLabelArg(0/*checkdelim*/);
5171 // find field
5172 StructField *fld = sth->fields;
5173 for (; fld; fld = fld->next) if (fld->name && strcmp(fld->name, nstr) == 0) break;
5174 if (!fld) fatal("unknown field `%s` in struct `%s`", nstr, sth->name);
5176 // only `=` is allowed
5177 matchDelim('=');
5179 int defined = 1;
5180 int32_t ival = getExprArg(&defined, NULL);
5182 if (fld->size == 1) {
5183 if (defined && (ival < -128 || ival > 255)) {
5184 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5185 (fld->name ? fld->name : "<anonymous>"), ival);
5187 if (ival < -128) ival = -128;
5188 const uint8_t v = (ival < 0 ? 0x100+ival : ival)&0xff;
5189 putByte(origPC+fld->ofs, v);
5190 } else if (fld->size == 2) {
5191 if (defined && (ival < -32768 || ival > 65535)) {
5192 fatal("STRUCT: field `%s` has inivalid initial value: %d",
5193 (fld->name ? fld->name : "<anonymous>"), ival);
5195 if (ival < -32768) ival = -32768;
5196 const uint16_t v = (ival < 0 ? 0x10000+ival : ival)&0xffff;
5197 putWord(origPC+fld->ofs, v);
5198 } else if (fld->size == 4) {
5199 uint32_t v = 0;
5200 memcpy(&v, &ival, 4);
5201 putWord(origPC+fld->ofs, v&0xffff);
5202 putWord(origPC+fld->ofs+2, (v>>16)&0xffff);
5203 } else {
5204 fatal("STRUCT `%s`: trying to init some strangely-sized field", sth->name);
5209 return PI_SKIP_LINE;
5213 static int structCheckSizeDecl (const char *s) {
5214 if (strEquCI(s, "byte")) return 1;
5215 if (strEquCI(s, "word")) return 2;
5216 if (strEquCI(s, "address")) return 2;
5217 if (strEquCI(s, "dword")) return 4;
5218 if (strEquCI(s, "label")) return 0;
5219 if (strEquCI(s, "defs")) return -666;
5220 if (strEquCI(s, "ds")) return -666;
5221 return -1;
5225 // STRUCT name [,init_offset]
5226 static int piSTRUCT (void) {
5227 int zerofill = 0, extend = 0;
5228 int defined = 1, inum;
5229 SourceLine *stline;
5231 //fprintf(stderr, "PASS=%d\n", pass);
5232 if (pass) {
5233 stline = currSrcLine;
5234 nextSrcLine(); // skip ourself
5235 if (!setCurSrcLine(findNextInstructionFromList(&inum, "ENDS", NULL))) {
5236 setCurSrcLine(stline);
5237 fatal("STRUCT: no ENDS");
5239 return PI_SKIP_LINE;
5243 if (checkDelim('*')) extend = 1;
5244 else if (checkDelim('!')) extend = 2; // compatibility with the old code
5245 else if (checkDelim('+')) extend = 2;
5248 // flags: `[flag, flag...]`
5249 // currently, only `zero_fill` flag is defined
5250 if (checkDelim('[')) {
5251 while (!checkDelim(']')) {
5252 if (zerofill == 0 && checkIdentCI("zero_fill")) zerofill = 1;
5253 else fatal("invalid structure flag");
5254 if (!checkDelim(',')) {
5255 if (!checkDelim(']')) fatal("invalid structure flag list");
5256 break;
5261 // if `extend` is before a struct name, we are extending it without inheriting
5262 if (checkIdentCI("extend") || checkIdentCI("extends")) extend = 1;
5264 // struct name
5265 char stname[128], estname[128];
5266 stname[0] = 0;
5267 char *nstr = getLabelArg(0/*checkdelim*/);
5268 if (!nstr || !nstr[0]) fatal("structure name expected");
5269 if (strlen(nstr) > 127) fatal("structure name too long");
5270 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr)) {
5271 fatal("structure name `%s` is invalid", nstr);
5273 strcpy(stname, nstr);
5275 StructInfo *sth = NULL;
5276 if (extend == 0) {
5277 for (sth = structList; sth; sth = sth->next) {
5278 if (strcmp(sth->name, stname) == 0) {
5279 fatal("duplicate struct name `%s`!", stname);
5282 if (urFindOp(stname)) fatal("invalid struct name `%s`!", stname);
5285 if (extend || checkIdentCI("extend") || checkIdentCI("extends")) {
5286 if (extend == 0) {
5287 // inherit
5288 extend = 2;
5289 nstr = getLabelArg(0/*checkdelim*/);
5290 if (!nstr || !nstr[0]) fatal("parent structure name expected");
5291 if (strlen(nstr) > 127) fatal("parent structure name too long");
5292 if (/*urFindOp(nstr) ||*/ !urasm_is_valid_name(nstr)) {
5293 fatal("structure name `%s` is invalid", nstr);
5295 strcpy(estname, nstr);
5296 for (sth = structList; sth; sth = sth->next) {
5297 if (strcmp(sth->name, estname) == 0) break;
5299 if (!sth) fatal("STRUCT: cannot extend unknown structure `%s`", estname);
5300 } else {
5301 // extend
5302 for (sth = structList; sth; sth = sth->next) {
5303 if (strcmp(sth->name, stname) == 0) break;
5305 if (!sth) fatal("STRUCT: cannot extend unknown structure `%s`", stname);
5307 // inherit?
5308 if (extend == 2) {
5309 StructInfo *stnew = calloc(1, sizeof(StructInfo));
5310 stnew->name = strdup(stname);
5311 stnew->size = sth->size;
5312 stnew->zerofill = (sth->zerofill || zerofill);
5314 // copy fields
5315 StructField *xlast = NULL;
5316 for (StructField *fld = sth->fields; fld; fld = fld->next) {
5317 StructField *nf = calloc(1, sizeof(StructField));
5318 nf->name = (fld->name ? strdup(fld->name) : NULL);
5319 nf->ofs = fld->ofs;
5320 nf->size = fld->size;
5321 nf->initValue = fld->initValue;
5322 nf->hasInit = fld->hasInit;
5323 if (xlast) xlast->next = nf; else stnew->fields = nf;
5324 xlast = nf;
5327 sth = stnew;
5329 } else {
5330 // allocate struct header
5331 sth = calloc(1, sizeof(StructInfo));
5332 sth->name = strdup(stname);
5333 sth->zerofill = zerofill;
5336 int32_t currofs = (sth ? sth->size : 0);
5337 // parse initial offset
5338 if (checkDelim(',')) {
5339 defined = 1;
5340 currofs = getExprArg(&defined, NULL);
5341 if (!defined) fatal("STRUCT: initial offset must be defined");
5342 if (currofs < 0 || currofs > 65535) fatal("STRUCT: invalid initial offset");
5345 // done with args
5346 if (!isLineEnd()) fatal("too many arguments for STRUCT");
5348 // for fast appending
5349 StructField *fldlast = (sth ? sth->fields : NULL);
5350 StructField *fldnew = NULL;
5352 if (fldlast) {
5353 while (fldlast->next) fldlast = fldlast->next;
5356 if (extend == 2) fldnew = sth->fields;
5358 // parse until ENDS
5359 stline = currSrcLine;
5360 nextSrcLine(); // skip ourself
5361 //fprintf(stderr, "001: <%s>\n", currLine);
5362 inum = -1;
5363 while (currSrcLine) {
5364 size_t slen = strlen(currLine);
5365 currLineRemoveComment();
5366 currLineTrimBlanks();
5367 if (!currLine[0]) { nextSrcLine(); continue; }
5369 if (strIsCommand("ENDS", currSrcLine->line)) break;
5370 //fprintf(stderr, "112: <%s> (%d)\n", currLine, inum);
5372 // parse new field
5373 int skipInit = 0;
5374 char *fldname = getLabelArg(0/*checkdelim*/);
5375 int fldsize = structCheckSizeDecl(fldname);
5376 if (fldsize == -666) {
5377 // defs/ds
5378 defined = 1;
5379 fldsize = getExprArg(&defined, NULL);
5380 if (!defined) fatal("STRUCT: DEFS size must be defined");
5381 if (fldsize < 0 || fldsize > 65535) fatal("STRUCT: DEFS size is invalid");
5382 skipInit = 1;
5383 fldname = NULL;
5384 } else if (fldsize < 0) {
5385 // this must be a label
5386 int found = 0;
5387 for (StructField *cf = sth->fields; cf; cf = cf->next) {
5388 if (cf->name && cf->name[0] && strcmp(cf->name, fldname) == 0) {
5389 found = 1;
5390 break;
5393 if (found) fatal("duplicate field name '%s'", fldname);
5394 fldname = strdup(fldname);
5395 char *szdecl = getLabelArg(0/*checkdelim*/);
5396 fldsize = structCheckSizeDecl(szdecl);
5397 if (fldsize == -666) {
5398 // defs/ds
5399 defined = 1;
5400 fldsize = getExprArg(&defined, NULL);
5401 if (!defined) fatal("STRUCT: DEFS size must be defined");
5402 if (fldsize < 0 || fldsize > 65535) fatal("STRUCT: DEFS size is invalid");
5403 skipInit = 1;
5404 } else {
5405 if (fldsize < 0) fatal("field size definition expected");
5407 } else {
5408 fldname = NULL; // no name
5411 // check for init value
5412 int hasInit = 0;
5413 int32_t initval = 0;
5415 if (!skipInit && checkDelim('=')) {
5416 defined = 1;
5417 initval = getExprArg(&defined, NULL);
5418 if (!defined) fatal("STRUCT: initial value must be defined");
5419 hasInit = 1;
5420 if (fldsize == 0 ||
5421 (fldsize == 1 && (initval < -128 || initval > 255)) ||
5422 (fldsize == 2 && (initval < -32768 || initval > 65535)))
5424 fatal("STRUCT: initial value is out of range");
5428 if (!isLineEnd()) fatal("STRUCT: extra field data");
5430 if (currofs+fldsize > 65535) fatal("STRUCT: too big");
5432 // create field
5433 StructField *fld = calloc(1, sizeof(StructField));
5434 fld->name = fldname;
5435 fld->ofs = currofs;
5436 fld->size = fldsize;
5437 fld->hasInit = hasInit;
5438 fld->initValue = initval;
5439 if (fldlast) fldlast->next = fld; else sth->fields = fld;
5440 fldlast = fld;
5441 currofs += fldsize;
5443 if (!fldnew) fldnew = fld;
5445 #if 0
5446 fprintf(stderr, "FLD <%s>: ofs=%u; size=%u; hasinit=%d; init=%d\n", fld->name, fld->ofs,
5447 fld->size, fld->hasInit, fld->initValue);
5448 #endif
5450 nextSrcLine();
5452 if (!currSrcLine) { setCurSrcLine(stline); fatal("no ENDS"); }
5453 if (currofs > 65535) fatal("STRUCT: too big");
5454 sth->size = currofs;
5456 //FIXME: structs must be local to module!
5458 // register new struct
5459 if (extend == 0 || extend == 2) {
5460 StructInfo *slast = structList;
5461 if (slast) {
5462 while (slast->next) slast = slast->next;
5463 slast->next = sth;
5464 } else {
5465 structList = sth;
5467 UrAsmOp *op = urAddOp(sth->name, &piSTRUCT_Internal);
5468 op->udata = sth;
5471 // add struct labels
5472 for (StructField *fld = fldnew; fld; fld = fld->next) {
5473 if (!fld->name || !fld->name[0]) continue;
5475 char *nn = mangleLabelName(sth->name);
5476 char *lbn = malloc(strlen(nn)+strlen(fld->name)+64);
5477 sprintf(lbn, "%s.%s", nn, fld->name);
5479 //fprintf(stderr, "%d: <%s>\n", extend, lbn);
5481 UrLabelInfo *c = urFindLabel(lbn);
5482 if (c) fatal("STRUCT: field name '%s' conflict!", fld->name);
5484 c = urAddLabel(lbn);
5485 free(lbn);
5486 c->type = LBL_TYPE_STOFS;
5487 c->value = fld->ofs;
5488 c->known = 1;
5491 // add `structname.sizeof`
5493 char *nn = mangleLabelName(sth->name);
5494 char *lbn = malloc(strlen(nn)+64);
5495 sprintf(lbn, "%s.%s", nn, "_sizeof");
5497 UrLabelInfo *c = urFindLabel(lbn);
5498 if (c) {
5499 if (!extend) fatal("STRUCT: field name '%s' conflict!", "_sizeof");
5500 } else {
5501 c = urAddLabel(lbn);
5503 free(lbn);
5505 c->type = LBL_TYPE_STOFS;
5506 c->value = sth->size;
5507 c->known = 1;
5510 return PI_SKIP_LINE;
5514 // ////////////////////////////////////////////////////////////////////////// //
5515 static int piEND_PASMO (void) {
5516 int defined = 1;
5517 int32_t res = getOneExprArg(&defined, NULL);
5518 //if (!defined) fatal("sorry, ENT operand value must be known here");
5519 if (res < 0 || res > 65535) fatal("invalid END operand value: %d", res);
5520 ent = res;
5521 return PI_CONT_LINE;
5525 ///////////////////////////////////////////////////////////////////////////////
5526 // DUP, EDUP
5528 static int piEDUP (void) {
5529 fatal("EDUP without DUP");
5530 checkOperatorEnd();
5531 return PI_SKIP_LINE;
5535 static int piENDR (void) {
5536 fatal("ENDR without REPT");
5537 checkOperatorEnd();
5538 return PI_SKIP_LINE;
5542 // DUP count [,var [,initval [,incr]]]
5543 static int piDUPEx (int isrept) {
5544 int defined = 1, inum;
5545 SourceLine *stline, *eline = NULL;
5547 //int32_t cnt = getOneExprArg(&defined, NULL);
5548 int32_t cnt = getExprArg(&defined, NULL);
5549 if (!defined) fatal("DUP: counter must be defined");
5550 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
5551 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
5553 int32_t initval = 0;
5554 int32_t incr = 1;
5556 // variable name
5557 char vname[128];
5558 vname[0] = 0;
5559 if (currLine[0] == ',') {
5560 (void)eatComma();
5561 char *lbl = getLabelArg(0/*checkdelim*/);
5562 if (lbl[0]) {
5563 if (strlen(lbl) > 127) fatal("DUP counter variable name too long");
5564 if (urFindOp(lbl) || !urasm_is_valid_name(lbl)) fatal("DUP counter variable name is invalid");
5565 strcpy(vname, lbl);
5569 // initial variable value
5570 if (currLine[0] == ',') {
5571 (void)eatComma();
5572 defined = 1;
5573 initval = getExprArg(&defined, NULL);
5574 if (!defined) fatal("DUP: initial value must be defined");
5577 // increment
5578 if (currLine[0] == ',') {
5579 (void)eatComma();
5580 defined = 1;
5581 incr = getExprArg(&defined, NULL);
5582 if (!defined) fatal("DUP: increment value must be defined");
5585 if (currLine[0]) fatal("too many arguments for DUP");
5587 int dupstack[128];
5588 int dupsp = 0;
5590 // now find corresponding EDUP
5591 // note that we should skip nested DUPs
5592 nextSrcLine(); // skip ourself
5593 stline = currSrcLine;
5594 while (currSrcLine) {
5595 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "REPT", "EDUP", "ENDR", "ENDM", NULL))) break;
5596 if (inum == 0 || inum == 1) {
5597 if (dupsp >= 128) fatal("too many nested DUPs");
5598 dupstack[dupsp++] = isrept;
5599 isrept = (inum == 1);
5600 nextSrcLine(); // skip DUP
5601 } else {
5602 // EDUP
5603 if (dupsp == 0) { eline = currSrcLine; break; }
5604 if (isrept) {
5605 if (inum < 3) fatal("invalid REPT end directive");
5606 } else {
5607 if (inum >= 3) fatal("invalid DUP end directive");
5609 isrept = dupstack[--dupsp];
5610 nextSrcLine(); // skip EDUP
5613 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
5615 // create counter local, if necessary
5616 UrLabelInfo *lcnt = (vname[0] ? urAddTempLocal(vname) : NULL);
5617 if (lcnt) lcnt->value = initval;
5619 // repeat lines
5620 while (cnt-- > 0) {
5621 setCurSrcLine(stline);
5622 while (currSrcLine != eline) processCurrentLine();
5623 // increment counter
5624 if (lcnt) lcnt->value += incr;
5627 // remove counter
5628 urRemoveTempLocal(lcnt);
5630 return PI_SKIP_LINE;
5634 static int piDUP (void) { return piDUPEx(0); }
5635 static int piREPT (void) { return piDUPEx(1); }
5638 ///////////////////////////////////////////////////////////////////////////////
5639 // IF, ENDIF
5641 static int ifCount = 0;
5644 // results:
5645 // -1: error (should not happen)
5646 // 0: successfully complete
5647 // 1: stopped *AT* "ELSE"
5648 // 2: stopped *AT* "ELSIF"
5649 static int ifSkipToEndIfOrElse (int wholeBody) {
5650 int inum, wasElse = 0;
5651 SourceLine *oline;
5653 while (currSrcLine) {
5654 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF",
5655 "ELSEIF", "MACRO", "ENDM", "IFX",
5656 "ELSEIFX",
5657 "ELSIF", "ELSIFX",
5658 NULL))) break;
5659 switch (inum) {
5660 case 0: /* if */
5661 case 6: /* ifx */
5662 nextSrcLine(); // skip IF
5663 ifSkipToEndIfOrElse(1); // and recurse
5664 nextSrcLine(); // skip ENDIF
5665 break;
5666 case 1: /* else */
5667 if (wasElse) fatal("duplicate ELSE");
5668 if (!wholeBody) return 1;
5669 wasElse = 1;
5670 nextSrcLine(); // skip ELSE
5671 break; // and continue
5672 case 2: /* endif */
5673 return 0; // and exit
5674 case 3: /* elseif */
5675 case 7: /* elseifx */
5676 case 8: /* elsif */
5677 case 9: /* elsifx */
5678 if (wasElse) fatal("ELSIF in ELSE");
5679 if (!wholeBody) return 2;
5680 nextSrcLine(); // skip ELSIF
5681 break; // and continue
5682 case 4: /* macro */
5683 // skip it as a whole
5684 nextSrcLine(); // skip MACRO
5685 for (;;) {
5686 oline = currSrcLine;
5687 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
5688 if (inum == 1) break;
5689 fatal("invalid nested MACRO");
5691 nextSrcLine(); // skip ENDM
5692 break;
5693 case 5: /* endm */
5694 fatal("unexpected ENDM");
5697 fatal("IF without ENDIF");
5698 return -1;
5702 static int piENDIF (void) {
5703 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5704 if (--ifCount < 0) fatal("ENDIF without IF");
5705 checkOperatorEnd();
5706 return PI_SKIP_LINE;
5710 static int piELSE (void) {
5711 if (--ifCount < 0) fatal("ELSE without IF");
5712 nextSrcLine(); // skip ELSE
5713 ifSkipToEndIfOrElse(1);
5714 return PI_SKIP_LINE;
5717 static int piELSIF (void) {
5718 if (--ifCount < 0) fatal("ELSIF without IF");
5719 nextSrcLine(); // skip ELSIF
5720 ifSkipToEndIfOrElse(1);
5721 return PI_SKIP_LINE;
5725 static int piELSIFX (void) {
5726 if (--ifCount < 0) fatal("ELSIFX without IF");
5727 nextSrcLine(); // skip ELSIFX
5728 ifSkipToEndIfOrElse(1);
5729 return PI_SKIP_LINE;
5733 static int piIFAll (int isIfX) {
5734 int defined = 1;
5735 int ooo = lblOptMakeU2;
5737 //fprintf(stderr, "piIFALL: <%s>\n", currLine);
5738 lblOptMakeU2 = (isIfX ? 1 : 0);
5739 int32_t cond = getOneExprArg(&defined, NULL);
5740 lblOptMakeU2 = ooo;
5741 //fprintf(stderr, "piIFALL COND: <%d>\n", cond);
5743 if (!defined) {
5744 if (!isIfX) fatal("IF: condition must be defined");
5745 cond = 0; // for IFX: 0 if there is any undefined label
5747 if (cond) {
5748 // ok, do it until ELSE/ELSIF/ENDIF
5749 ++ifCount;
5750 return PI_SKIP_LINE;
5752 for (;;) {
5753 int r;
5754 char *args;
5756 nextSrcLine(); // skip last instruction
5757 // skip until ELSE/ELSIF/ENDIF
5758 r = ifSkipToEndIfOrElse(0);
5759 /*!fprintf(stderr, "ELSIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5760 if (r == 0) break; // ENDIF
5761 if (r == 1) { ++ifCount; break; } // ELSE
5762 // ELSIF, do condition
5763 args = strIsCommand("ELSIF", currLine);
5764 if (args == NULL) args = strIsCommand("ELSEIF", currLine);
5765 if (args != NULL) {
5766 isIfX = 0;
5767 } else {
5768 args = strIsCommand("ELSIFX", currLine);
5769 if (args == NULL) args = strIsCommand("ELSEIFX", currLine);
5770 if (args == NULL) fatal("internal error in conditionals"); // the thing that should not be
5771 isIfX = 1;
5773 memmove(currLine, args, strlen(args)+1);
5774 /*!fprintf(stderr, "ELSIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0));*/
5775 cond = getOneExprArg(&defined, NULL);
5776 /*!fprintf(stderr, "ELSIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (currSrcLine ? currSrcLine->lineNo : 0), cond);*/
5777 if (!defined) {
5778 if (!isIfX) fatal("ELSIF: condition must be defined");
5779 cond = 0; // for IFX: 0 if there is any undefined label
5781 if (cond) { ++ifCount; break; } // condition is true
5783 return PI_SKIP_LINE;
5787 static int piIF (void) { return piIFAll(0); }
5788 static int piIFX (void) { return piIFAll(1); }
5791 ///////////////////////////////////////////////////////////////////////////////
5792 // macro processor
5793 ///////////////////////////////////////////////////////////////////////////////
5795 what i did with MACRO is the brain-damaged cheating all the way.
5797 first, i will collect the MACRO body and remember it (removing it
5798 from the main source code, so second pass will not see it).
5800 second, when the macro is used, i will:
5801 * insert the whole macro body in place (label resolver will
5802 fix "..lbl" labels for us)
5803 * let the asm play with it
5805 this is not the best scheme, but it is fairly simple and it works.
5808 // will be called when parser encounters term starting with '=' or '*'
5809 // first term char will not be skipped
5810 // must return pointer to the first char after expression end
5811 static const char *getValueCB (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
5812 char name[257];
5813 char *p = name;
5815 if (curmacro == NULL) fatal("'=' outside of macro");
5816 if (*expr++ != '=') fatal("'=' expected!");
5818 expr = strSkipSpaces(expr);
5819 while (*expr) {
5820 if (isAlphaDigit(*expr) || *expr == '_') {
5821 if (p-name > 250) fatal("id too long");
5822 *p++ = *expr++;
5823 continue;
5825 break;
5827 *p = 0;
5829 expr = strSkipSpaces(expr);
5830 for (int f = 0; f < curmacro->mac->argc; ++f) {
5831 if (strEquCI(name, curmacro->mac->argnames[f])) {
5832 if (*expr == '[') {
5833 urasm_exprval_t v;
5834 int l = (int)strlen(curmacro->argvals[f]);
5835 ++expr; // skip "["
5836 urasm_exprval_init(&v);
5837 expr = urasm_expr_ex(&v, expr, addr, &donteval, defined, error);
5838 if (*error) return expr;
5839 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
5840 ++expr;
5841 if (v.val < 0) v.val += l;
5842 if (v.val < 0 || v.val >= l) {
5843 res->val = '?';
5844 } else {
5845 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
5847 return expr;
5848 } else {
5849 urasm_expr_ex(res, curmacro->argvals[f], addr, &donteval, defined, error);
5850 return expr;
5855 fatal("unknown macro variable: '%s'", name);
5859 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
5860 // opr starts with '=' (invariant)
5861 static int expandCB (char *opr, int oprlen) {
5862 char name[257], *p = name, *op = opr;
5864 if (curmacro == NULL) fatal("'=' outside of macro");
5865 if (*op++ != '=') fatal("'=' expected!"); // just in case
5866 //fprintf(stderr, "expand: [%s]\n", opr);
5868 if (!isAlpha(op[0])) return 0; // nothing to do
5870 // copy argument name
5871 while (*op) {
5872 if (isAlphaDigit(*op) || *op == '_') {
5873 if (p-name > 250) fatal("id too long");
5874 *p++ = *op++;
5875 continue;
5877 break;
5879 *p = 0;
5881 // expand argument? we only need to expand `=arg[n]`
5882 op = strSkipSpaces(op);
5883 if (op[0] != '[') return 0;
5885 for (int f = 0; f < curmacro->mac->argc; ++f) {
5886 if (strEquCI(name, curmacro->mac->argnames[f])) {
5887 ur_assert(*op == '[');
5888 // replace argument with character, or with literal value
5889 // used for `=regpair[0]` or `=regpair[]`
5890 if (op[1] == ']') {
5891 op += 2;
5892 const int l = (int)strlen(curmacro->argvals[f]);
5893 const int lleft = (int)strlen(op);
5894 char *tmp = malloc(l+lleft+8);
5895 snprintf(tmp, l+lleft+8, "%s%s", curmacro->argvals[f], op);
5896 if ((int)strlen(tmp) > oprlen) {
5897 free(tmp);
5898 return -1;
5900 strcpy(opr, tmp);
5901 free(tmp);
5902 return 0;
5903 } else {
5904 urasm_exprval_t v;
5905 int error = 0, defined = 1, donteval = 0;
5906 ++op; // skip '['
5907 urasm_exprval_init(&v);
5908 op = (char *)urasm_expr_ex(&v, op, pc, &donteval, &defined, &error);
5909 if (error) return -1;
5910 // result should be a number
5911 if (op == NULL || *op != ']' || v.str != NULL) return -1;
5912 ++op; // skip ']'
5913 // it is guaranteed to have more than one char in opr
5914 // so we can simply put char from argument value, and remove other expression chars
5915 const int l = (int)strlen(curmacro->argvals[f]);
5916 if (v.val < 0) v.val += l; // negative: indexing from the end
5917 if (v.val < 0 || v.val >= l) fatal("index %d is out of bounds for macro argument '%s'", v.val, curmacro->mac->argnames[f]);
5918 // copy char
5919 opr[0] = curmacro->argvals[f][v.val];
5920 // remove other chars
5921 memmove(opr+1, op, strlen(op)+1);
5923 return 0;
5927 fatal("unknown macro variable: '%s'", name);
5931 // main macro expander
5932 static void processMacro (MacroDef *mc) {
5933 SourceLine *oldcurline = currSrcLine;
5934 CurMacroDef cm;
5935 memset(&cm, 0, sizeof(cm));
5937 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
5938 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
5940 // parse macro arguments
5941 cm.mac = mc;
5942 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) cm.argvals[f] = NULL;
5943 int currArg = 0;
5944 for (;;) {
5945 removeSpaces();
5946 // do we have more arguments?
5947 if (!currLine[0]) break;
5948 // check for argument name (this is purely cosmetic thing)
5949 // use like this: "mcall :argname=[value]" (value may be omited for default)
5950 if (currLine[0] == ':' && isAlpha(currLine[1])) {
5951 int pos = 2;
5952 while (isAlphaDigit(currLine[pos]) || currLine[pos] == '_') ++pos;
5953 //hack!
5954 const char svch = currLine[pos];
5955 currLine[pos] = 0;
5956 if (!strEquCI(currLine+1, mc->argnames[currArg])) {
5957 // out-of-order, find proper argument and rewind to it
5958 currArg = -1;
5959 for (int c = 0; c < mc->argc; ++c) {
5960 if (strEquCI(currLine+1, mc->argnames[c])) {
5961 currArg = c;
5962 break;
5965 if (currArg < 0) fatal("macro '%s' has no argument named '%s'", mc->name, currLine+1);
5967 currLine[pos] = svch;
5968 // remove argument name
5969 memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
5970 removeSpaces();
5971 // check for '='
5972 if (currLine[0] != '=') fatal("expected '=' after argument name");
5973 // remove '='
5974 memmove(currLine, currLine+1, strlen(currLine));
5975 removeSpaces();
5977 // too many args?
5978 if (currArg >= mc->argc) fatal("too many arguments to macro '%s'", mc->name);
5979 // check for default value
5980 if (currLine[0] == ',') {
5981 if (mc->argdefaults[currArg] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[currArg]);
5982 cm.argvals[currArg] = strdup(mc->argdefaults[currArg]);
5983 memmove(currLine, currLine+1, strlen(currLine));
5984 } else {
5985 // skip argument (so we will know its length, and will be able to copy it)
5986 char *e = skipMacroArg(currLine);
5987 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
5988 const char ech = *e;
5989 if (ech != 0) {
5990 if (ech == ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc->name);
5991 if (ech != ',') fatal("invalid invocation of macro '%s'", mc->name);
5993 *e = 0;
5994 cm.argvals[currArg] = strdup(currLine);
5995 // strip trailing spaces
5996 strTrimRight(cm.argvals[currArg]);
5997 if (ech) {
5998 *e = ech;
5999 memmove(currLine, e+1, strlen(e));
6000 } else {
6001 currLine[0] = 0;
6004 ++currArg;
6006 // check for line end
6007 removeSpaces();
6008 if (currLine[0]) fatal("invalid macro invocation");
6010 // setup default argument values
6011 for (int f = 0; f < mc->argc; ++f) {
6012 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
6013 if (cm.argvals[f]) continue;
6014 if (mc->argdefaults[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
6015 cm.argvals[f] = strdup(mc->argdefaults[f]);
6016 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
6019 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
6021 // insert macro code into the source
6022 setCurSrcLine(mc->lines);
6023 curmacro = &cm;
6024 ++curmacronum;
6025 while (currSrcLine != NULL) {
6026 //fprintf(stderr, "*[%s] (%d)\n", currSrcLine->line, currSrcLine->lineNo);
6027 processCurrentLine();
6030 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) if (cm.argvals[f]) free(cm.argvals[f]);
6031 setCurSrcLine(oldcurline);
6032 curmacro = NULL;
6033 nextSrcLine();
6037 static int piMACRO (void) {
6038 char *name;
6039 int argc = 0;
6040 char *argdefaults[MAX_MACRO_ARGS];
6041 char *argnames[MAX_MACRO_ARGS];
6042 SourceLine *stline, *eline = NULL;
6043 MacroDef *mc;
6045 name = strdup(getLabelArg(0));
6046 //fprintf(stderr, "[%s]\n", name);
6047 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
6048 if (currLine[0] == ',') memmove(currLine, currLine+1, strlen(currLine));
6049 removeSpaces();
6051 while (currLine[0]) {
6052 if (argc >= MAX_MACRO_ARGS) fatal("too many arguments in MACRO");
6053 if (!isAlpha(currLine[0])) fatal("invalid MACRO definition");
6054 argnames[argc] = strdup(getLabelArg(0));
6055 if (currLine[0] == '=') {
6056 // default value
6057 char *e = strchr(currLine, ','), tch;
6059 if (e == NULL) e = currLine+strlen(currLine);
6060 tch = *e;
6061 *e = 0;
6062 argdefaults[argc] = strdup(currLine+1);
6063 *e = tch;
6064 memmove(currLine, e, strlen(e)+1);
6065 } else {
6066 argdefaults[argc] = NULL;
6068 removeSpaces();
6069 if (currLine[0] == ',') {
6070 memmove(currLine, currLine+1, strlen(currLine));
6071 removeSpaces();
6073 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
6074 ++argc;
6077 // now find corresponding ENDM
6078 // note that we should skip nested DUPs
6079 stline = currSrcLine;
6080 nextSrcLine(); // skip ourself
6081 while (currSrcLine) {
6082 int inum;
6084 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
6085 // ok, we found something; what is it?
6086 if (inum == 0) {
6087 // new MACRO
6088 fatal("no nested MACROs yet");
6089 } else {
6090 // ENDM, gotcha!
6091 eline = currSrcLine;
6092 // kill ENDM
6093 eline->line[0] = 0;
6094 nextSrcLine(); // skip ENDM
6095 break;
6098 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
6100 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
6101 mc->name = name;
6102 mc->argc = argc;
6103 for (int f = 0; f < argc; ++f) { mc->argdefaults[f] = argdefaults[f]; mc->argnames[f] = argnames[f]; }
6105 eline->next = NULL;
6106 mc->lines = stline->next;
6107 stline->next = currSrcLine;
6108 stline->line[0] = 0;
6110 mc->next = maclist;
6111 maclist = mc;
6112 setCurSrcLine(stline);
6113 return PI_SKIP_LINE;
6117 static int piENDM (void) {
6118 fatal("ENDM without MACRO");
6119 return PI_SKIP_LINE;
6123 ///////////////////////////////////////////////////////////////////////////////
6124 // line processor
6125 static void piTapParseLoaderName (void) {
6126 if (eatComma()) {
6127 int len;
6128 if (!isStrArg()) fatal("loader name expected");
6129 char *fn = getStrArg(&len);
6130 if (len > 10) fatal("loader name too long");
6131 memset(tapeLoaderName, ' ', 10);
6132 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
6137 static int piMATHMODE (void) {
6138 char *name = getOneLabelArg();
6139 if (strEquCI(name, "OLD")) urasm_use_old_priorities = 1;
6140 else if (strEquCI(name, "NEW")) urasm_use_old_priorities = 0;
6141 else fatal("invalid math mode; NEW or OLD expected");
6142 return PI_SKIP_LINE;
6146 static int piCASTRATES (void) {
6147 char *name = getOneLabelArg();
6148 if (strEquCI(name, "YES")) urasm_allow_hex_castrates = 1;
6149 else if (strEquCI(name, "TAN")) urasm_allow_hex_castrates = 1;
6150 else if (strEquCI(name, "NO")) urasm_allow_hex_castrates = 0;
6151 else if (strEquCI(name, "ONA")) urasm_allow_hex_castrates = 0;
6152 else fatal("yes/no value expected");
6153 return PI_SKIP_LINE;
6157 static int piREFOPT (void) {
6158 char *name = getOneLabelArg();
6159 if (strEquCI(name, "ALLLABELS")) urasm_dump_all_labels = 1;
6160 else if (strEquCI(name, "ALL_LABELS")) urasm_dump_all_labels = 1;
6161 else if (strEquCI(name, "NOUPCASE")) urasm_dump_all_labels = 0;
6162 else if (strEquCI(name, "NO_UPCASE")) urasm_dump_all_labels = 0;
6163 else if (strEquCI(name, "DEFAULT")) urasm_dump_all_labels = 1;
6164 else fatal("invalid refopt mode");
6165 return PI_SKIP_LINE;
6169 static int piDEFFMT (void) {
6170 char *name;
6172 if (isStrArg()) {
6173 int len = 0;
6174 name = getStrArg(&len);
6175 } else {
6176 name = getLabelArg(1);
6178 if (optWTChanged) return 1;
6180 optRunDMB = optRunTape = optRunSCL = optRunDSK = 0;
6181 //optRunSNA = 0;
6183 if (strEquCI(name, "SNA") || strEquCI(name, "RUNSNA") || strEquCI(name, "SNARUN")) {
6184 optWriteType = 's';
6185 if (currLine[0]) fatal("too many expressions");
6186 return PI_SKIP_LINE;
6188 if (strEquCI(name, "TAP") || strEquCI(name, "TAPE")) {
6189 optWriteType = 't';
6190 piTapParseLoaderName();
6191 return PI_SKIP_LINE;
6193 if (strEquCI(name, "RUNTAP") || strEquCI(name, "RUNTAPE") || strEquCI(name, "TAPERUN")) {
6194 optRunTape = 1;
6195 optWriteType = 't';
6196 piTapParseLoaderName();
6197 return PI_SKIP_LINE;
6199 if (strEquCI(name, "BIN") || strEquCI(name, "RAW")) {
6200 optWriteType = 'r';
6201 if (currLine[0]) fatal("too many expressions");
6202 return PI_SKIP_LINE;
6204 if (strEquCI(name, "DMB") || strEquCI(name, "RUNDMB") || strEquCI(name, "DMBRUN")) {
6205 optRunDMB = (name[3] != 0);
6206 optWriteType = 'd';
6207 if (currLine[0]) fatal("too many expressions");
6208 return PI_SKIP_LINE;
6210 if (strEquCI(name, "NONE") || strEquCI(name, "NOTHING")) {
6211 optWriteType = 'n';
6212 if (currLine[0]) fatal("too many expressions");
6213 return PI_SKIP_LINE;
6215 if (strEquCI(name, "SCL") || strEquCI(name, "RUNSCL") || strEquCI(name, "SCLRUN")) {
6216 optWriteType = 'S';
6217 optRunSCL = (name[3] != 0 ? -1 : 0); /* no boot */
6218 piTapParseLoaderName();
6219 return PI_SKIP_LINE;
6221 if (strEquCI(name, "SCLMONO") || strEquCI(name, "RUNSCLMONO") || strEquCI(name, "SCLMONORUN")) {
6222 optWriteType = 'M';
6223 optRunSCL = (name[7] != 0 ? -1 : 0); /* no boot */
6224 piTapParseLoaderName();
6225 return PI_SKIP_LINE;
6227 if (strEquCI(name, "SCLBOOT") || strEquCI(name, "BOOTSCL")) {
6228 optWriteType = 'S';
6229 optRunSCL = 1; /* with boot */
6230 piTapParseLoaderName();
6231 return PI_SKIP_LINE;
6233 if (strEquCI(name, "SCLMONOBOOT") || strEquCI(name, "BOOTSCLMONO")) {
6234 optWriteType = 'M';
6235 optRunSCL = 1; /* with boot */
6236 piTapParseLoaderName();
6237 return PI_SKIP_LINE;
6240 if (strEquCI(name, "DSKBOOT") || strEquCI(name, "BOOTDSK") ||
6241 strEquCI(name, "DSK180BOOT") || strEquCI(name, "BOOTDSK180"))
6243 optWriteType = '3';
6244 optRunDSK = 1; /* with boot */
6245 optDskType = P3DSK_PCW_SS; // +3DOS 180K disk
6246 piTapParseLoaderName();
6247 return PI_SKIP_LINE;
6250 if (strEquCI(name, "DSK720BOOT") || strEquCI(name, "BOOTDSK720")) {
6251 optWriteType = '3';
6252 optRunDSK = 1; /* with boot */
6253 optDskType = P3DSK_PCW_DS; // +3DOS 720K disk
6254 piTapParseLoaderName();
6255 return PI_SKIP_LINE;
6258 if (strEquCI(name, "DSK") || strEquCI(name, "DSK180")) {
6259 optWriteType = '3';
6260 optRunDSK = 0; /* without boot */
6261 optDskType = P3DSK_PCW_SS; // +3DOS 180K disk
6262 piTapParseLoaderName();
6263 return PI_SKIP_LINE;
6266 if (strEquCI(name, "DSK720")) {
6267 optWriteType = '3';
6268 optRunDSK = 0; /* without boot */
6269 optDskType = P3DSK_PCW_DS; // +3DOS 720K disk
6270 piTapParseLoaderName();
6271 return PI_SKIP_LINE;
6274 fatal("invalid default output type: %s", name);
6278 ///////////////////////////////////////////////////////////////////////////////
6279 // line processor
6281 static void processCurrentLine (void) {
6282 if (!currSrcLine) return; // do nothing
6283 loadCurSrcLine();
6284 ur_assert(asmMode != AMODE_UFO); // the thing that should not be
6285 processLabel();
6286 for (;;) {
6287 char *str, *ee, name[66];
6288 UrAsmOp *op;
6289 int len;
6290 const char *errpos;
6291 removeSpacesAndColons();
6292 // skip spaces and ':'
6293 if (!currLine[0]) { nextSrcLine(); break; }
6294 // try to find and process command
6295 str = currLine; //while (*str && isSpace(*str)) ++str; // skip spaces
6296 // find command end
6297 for (ee = str; *ee && !isSpace(*ee) && *ee != '"' && *ee != '\'' && *ee != ',' && *ee != ':'; ++ee) {}
6298 // get command, if any
6299 if (ee != str && ee-str <= 64) {
6300 MacroDef *mc;
6301 memset(name, 0, sizeof(name));
6302 memmove(name, str, ee-str);
6303 /* UrForth macro? */
6304 uint32_t fwmacro = ufoIsMacro(name);
6305 if (fwmacro != 0) {
6306 // ok, do it
6307 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6308 memmove(currLine, str, strlen(str)+1);
6309 ufoMacroRun(fwmacro, currLine, currSrcLine->fname, currSrcLine->lineNo);
6310 /* only one macro per line! */
6311 nextSrcLine(); // skip it
6312 break;
6314 /* known command? */
6315 op = urFindOp(name);
6316 if (op) {
6317 // ok, do it
6318 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6319 memmove(currLine, str, strlen(str)+1);
6320 urCurrentOp = op;
6321 if (op->fn()) {
6322 urCurrentOp = NULL;
6323 nextSrcLine(); // skip it
6324 break;
6326 urCurrentOp = NULL;
6327 continue;
6329 /* known macro? */
6330 if ((mc = findMacro(name)) != NULL) {
6331 // ok, do it
6332 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
6333 memmove(currLine, str, strlen(str)+1);
6334 processMacro(mc);
6335 /* only one macro per line! */
6336 break;
6339 // with reservation management
6340 again_from_reserve:
6341 len = urasm_opasm(currLine, pc, disp, &errpos);
6342 if (len < 0) fatalUrLib(len);
6343 // check for reservation
6344 if (len > 0) {
6345 if (reserveHit(pc, len)) {
6346 insert_jump:
6347 if (pc != disp) fatal("trying to use reserved areas with phased code");
6348 reserveReleaseUsed(pc, len);
6349 int nextaddr = reservedFindNextSuitableFreeFrom(pc, pc+len);
6350 if (nextaddr < 0) fatal("codegen started in reserved area #%04X", pc);
6351 // insert jump to the new area, and retry
6352 fprintf(stderr, "...inserting jump from #%04X to #%04X\n", (unsigned)pc, (unsigned)nextaddr);
6353 // can we insert a JP? it is faster than JR
6354 if (memresv[pc] || memresv[pc+1]) fatal("internal reserve area manager error at #%04X", pc);
6355 if (!memresv[pc+2]) {
6356 // use JP
6357 emitByte(0xc3U); // JP
6358 emitWord((uint16_t)nextaddr);
6359 } else {
6360 // use JR
6361 int jrdest = nextaddr-(pc+2U);
6362 if (jrdest >= 128) fatal("internal reserve area manager error at #%04X (jr dist)", pc);
6363 emitByte(0x18U); // JR
6364 emitByte((uint8_t)jrdest);
6366 disp = pc = (uint16_t)nextaddr;
6367 goto again_from_reserve;
6368 } else {
6369 // did we compiled JP/JR?
6370 if (getByte(pc) == 0xc3U || getByte(pc) == 0x18U) {
6371 // no room for jump?
6372 if (memresv[(pc+len)&0xffffU] || memresv[(pc+len+1)&0xffffU] || memresv[(pc+len+2)&0xffffU]) {
6373 if (pc != disp) fatal("trying to use reserved areas with phased code");
6374 // find next free area
6375 int next = pc+len;
6376 while (next >= 0 && next < 65536) {
6377 next = reserveFindFreeFrom(next);
6378 if (next < 0) break;
6379 // has room for JP there?
6380 if (!memresv[next] && !memresv[(next+1)&0xffffU] && !memresv[(next+2)&0xffffU]) break;
6381 // no room for JP, has room for JP?
6382 if (!memresv[next] && !memresv[(next+1)&0xffffU]) {
6383 // check if jr can fit
6384 const int nj = reservedFindNextJumpableFreeFrom(next, next+2);
6385 if (nj >= 0) break;
6387 next = reserveFindReservedFrom(next);
6389 if (next >= 0) disp = pc = (next-len)&0xffffU; // so "+len" will make PC right
6391 } else {
6392 // check if we still have enough room for a possible jp/jr
6393 if (memresv[(pc+len)&0xffffU] || memresv[(pc+len+1)&0xffffU]) goto insert_jump;
6394 // we have at least a room for JR, check if we have a room for JP
6395 if (!memresv[(pc+len+2)&0xffffU]) {
6396 // no room for JP, check if jr will be enough
6397 int next = reservedFindNextSuitableFreeFrom(pc, pc+len+2);
6398 if (next >= 0 && next-(pc+len+2u) >= 128) goto insert_jump;
6403 pc += len;
6404 disp += len;
6405 if (len >= 0 && errpos) {
6406 memmove(currLine, errpos+1, strlen(errpos));
6407 } else {
6408 nextSrcLine(); // skip it
6409 break;
6415 ///////////////////////////////////////////////////////////////////////////////
6416 // start inline UrForth code
6418 static int piSTARTFORTH (void) {
6419 checkOperatorEnd();
6420 asmMode = AMODE_UFO;
6421 ufoInlineRun();
6422 asmMode = AMODE_NORMAL;
6423 return PI_SKIP_LINE;
6427 ///////////////////////////////////////////////////////////////////////////////
6428 // setup instructions
6430 static void registerInstructions (void) {
6431 urAddOpAndDollar("DISPLAY", piDISPLAY);
6432 urAddOpAndDollar("DISPLAY0", piDISPLAY0);
6433 urAddOpAndDollar("DISPLAYA", piDISPLAYA);
6434 urAddOpAndDollar("DISPHEX", piDISPHEX);
6435 urAddOpAndDollar("DISPHEX0", piDISPHEX0);
6436 urAddOpAndDollar("DISPHEXA", piDISPHEXA);
6438 urAddOpAndDollar("DEFFMT", piDEFFMT);
6439 urAddOp("$DEFFMT", piDEFFMT);
6440 urAddOp("$MODEL", piMODEL);
6441 urAddOp("$SAVECODE", piSAVECODE);
6443 urAddOp("MACRO", piMACRO);
6444 urAddOp("ENDM", piENDM);
6446 urAddOp("ORG", piORG);
6447 urAddOp("DISP", piDISP);
6448 urAddOp("ENDDISP", piENDDISP);
6449 urAddOp("PHASE", piDISP);
6450 urAddOp("DEPHASE", piENDDISP);
6451 urAddOp("UNPHASE", piENDDISP);
6452 urAddOp("ALIGN", piALIGN);
6453 urAddOp("DISPALIGN", piDISPALIGN);
6454 urAddOp("PHASEALIGN", piDISPALIGN);
6455 urAddOp("ENT", piENT);
6456 urAddOp("CLR", piCLR);
6457 urAddOp("RESERVE", piRESERVE);
6459 urAddOpAndDollar("INCLUDE", piINCLUDE);
6460 urAddOpAndDollar("INCBIN", piINCBIN);
6461 urAddOpAndDollar("DATABIN", piDATABIN);
6462 urAddOpAndDollar("CODEBIN", piCODEBIN);
6464 urAddOp("MODULE", piMODULE);
6465 urAddOp("ENDMODULE", piENDMODULE);
6467 urAddOp("DUP", piDUP);
6468 urAddOp("EDUP", piEDUP);
6469 urAddOp("REPT", piREPT);
6470 urAddOp("ENDR", piENDR);
6471 // structures
6472 urAddOp("STRUCT", piSTRUCT);
6473 urAddOp("ENDS", piENDS);
6474 // pasmo support
6475 urAddOp("END", piEND_PASMO);
6477 urAddOpAndDollar("IF", piIF);
6478 urAddOpAndDollar("IFX", piIFX);
6479 urAddOpAndDollar("ELSE", piELSE);
6480 urAddOpAndDollar("ELSIF", piELSIF);
6481 urAddOpAndDollar("ELSIFX", piELSIFX);
6482 urAddOpAndDollar("ELSEIF", piELSIF);
6483 urAddOpAndDollar("ELSEIFX", piELSIFX);
6484 urAddOpAndDollar("ENDIF", piENDIF);
6486 urAddOp("DEFINCR", piDEFINCR);
6487 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
6488 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
6489 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
6490 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
6491 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
6492 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
6493 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
6494 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
6496 urAddOp("$ERROR", piERROR);
6497 urAddOp("$WARNING", piWARNING);
6499 urAddOp("$PRINTF", piPRINTF);
6500 urAddOp("$PRINTF0", piPRINTF0);
6501 urAddOp("$PRINTFA", piPRINTFA);
6503 urAddOp("$MATHMODE", piMATHMODE);
6504 urAddOp("$REFOPT", piREFOPT);
6505 urAddOp("$CASTRATES", piCASTRATES);
6507 urAddOp("$START_FORTH", piSTARTFORTH);
6511 ///////////////////////////////////////////////////////////////////////////////
6512 // !0: invalid label
6514 static inline void fnSkipSpaces (const char *expr) {
6515 while (*expr && isSpace(*expr)) ++expr;
6516 return expr;
6521 UR_FORCE_INLINE char fnNextChar (const char *expr) {
6522 while (*expr && isSpace(*expr)) ++expr;
6523 return *expr;
6527 #define FN_SKIP_BLANKS do { \
6528 while (*expr && isSpace(*expr)) ++expr; \
6529 } while (0)
6531 #define FN_CHECK_END do { \
6532 while (*expr && isSpace(*expr)) ++expr; \
6533 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
6534 ++expr; \
6535 } while (0)
6538 #define FN_CHECK_COMMA do { \
6539 while (*expr && isSpace(*expr)) ++expr; \
6540 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
6541 ++expr; \
6542 } while (0)
6545 static const char *readLabelName (char *buf, const char *expr) {
6546 int pos = 0;
6548 while (*expr && isSpace(*expr)) ++expr;
6549 for (;;) {
6550 char ch = *expr++;
6552 if (pos >= 128) return NULL;
6553 if (ch == ')') { --expr; break; }
6554 if (!ch) break;
6555 if (isAlphaDigit(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
6556 buf[pos++] = ch;
6557 } else {
6558 break;
6561 if (pos < 1) return NULL;
6562 buf[pos] = '\0';
6563 if (!urasm_is_valid_name(buf)) return NULL;
6564 return expr;
6568 static const char *fnDefKn (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
6569 char lbl[130];
6571 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
6572 FN_CHECK_END;
6573 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
6574 return expr;
6577 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); }
6578 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); }
6581 static const char *fnAligned256 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6582 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6583 FN_CHECK_END;
6584 if (!donteval) res->val = (res->val%256 ? 0 : 1);
6585 return expr;
6589 static const char *fnSameSeg (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6590 urasm_exprval_t v0, v1;
6592 urasm_exprval_init(&v0);
6593 urasm_exprval_init(&v1);
6594 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
6595 if (*error) return expr;
6596 FN_CHECK_COMMA;
6597 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
6598 if (*error) return expr;
6599 FN_CHECK_END;
6600 if (!donteval) res->val = (v0.val/256 == v1.val/256);
6601 return expr;
6605 static const char *fnAlign (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6606 urasm_exprval_t v0, v1;
6607 urasm_exprval_init(&v0);
6608 urasm_exprval_init(&v1);
6609 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
6610 if (*error) return expr;
6611 FN_SKIP_BLANKS;
6612 if (fnNextChar(expr) == ',') {
6613 FN_CHECK_COMMA;
6614 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
6615 if (*error) return expr;
6616 } else {
6617 v1.val = 256;
6619 FN_CHECK_END;
6620 if (!donteval) {
6621 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
6622 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
6624 return expr;
6628 static const char *fnLow (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6629 const char *ee = expr;
6630 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6631 FN_CHECK_END;
6632 if (!donteval) {
6633 if (res->fixuptype == UR_FIXUP_HIBYTE) {
6634 *error = UR_EXPRERR_FUNC; return ee;
6636 res->fixuptype = UR_FIXUP_LOBYTE;
6637 res->val &= 0xff;
6639 return expr;
6643 static const char *fnHigh (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6644 const char *ee = expr;
6645 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6646 FN_CHECK_END;
6647 if (!donteval) {
6648 if (res->fixuptype == UR_FIXUP_LOBYTE) {
6649 *error = UR_EXPRERR_FUNC; return ee;
6651 res->fixuptype = UR_FIXUP_HIBYTE;
6652 res->val = (res->val>>8)&0xff;
6654 return expr;
6658 static const char *fnAbs (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6659 const char *ee = expr;
6660 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6661 FN_CHECK_END;
6662 if (!donteval) {
6663 if (res->fixuptype != UR_FIXUP_NONE) {
6664 *error = UR_EXPRERR_FUNC; return ee;
6666 res->val = abs(res->val);
6668 return expr;
6672 static const char *fnBSwap (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6673 const char *ee = expr;
6674 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6675 FN_CHECK_END;
6676 if (!donteval) {
6677 if (res->fixuptype != UR_FIXUP_NONE) {
6678 *error = UR_EXPRERR_FUNC; return ee;
6680 res->val = ((res->val>>8)&0xff)|((res->val&0xff)<<8);
6682 return expr;
6686 static const char *fnScrAddr8xn (int nmul, urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6687 int32_t scrbase = 0x4000;
6688 int32_t x, y;
6689 const char *ee = expr;
6690 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
6691 // first argument is `x`
6692 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6693 x = res->val;
6694 // second argument is `y`
6695 FN_CHECK_COMMA;
6696 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6697 y = res->val*nmul;
6698 // optional third arg is screen base
6699 FN_SKIP_BLANKS;
6700 if (expr[0] == ',') {
6701 FN_CHECK_COMMA;
6702 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6703 scrbase = res->val&0xffff;
6705 FN_CHECK_END;
6706 if (!donteval) {
6707 //urasm_exprval_clear(res);
6708 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
6709 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
6710 if (y < 0 || y > 191) fatal("invalid y coordinate: %d", y/nmul);
6711 res->val = scrbase+
6712 (y/64)*2048+
6713 (y%8)*256+
6714 ((y%64)/8)*32+
6716 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
6718 return expr;
6722 static const char *fnScrAddr8x1 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6723 return fnScrAddr8xn(1, res, expr, addr, donteval, defined, error);
6726 static const char *fnScrAddr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6727 return fnScrAddr8xn(8, res, expr, addr, donteval, defined, error);
6731 static const char *fnScrAttr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6732 int32_t scrbase = 0x4000;
6733 int32_t x, y;
6734 const char *ee = expr;
6735 // first argument is `x`
6736 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6737 x = res->val;
6738 // second argument is `y`
6739 FN_CHECK_COMMA;
6740 urasm_exprval_clear(res);
6741 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6742 y = res->val;
6743 // optional third arg is screen base
6744 FN_SKIP_BLANKS;
6745 if (expr[0] == ',') {
6746 FN_CHECK_COMMA;
6747 urasm_exprval_clear(res);
6748 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
6749 scrbase = res->val&0xffff;
6751 urasm_exprval_clear(res);
6752 FN_CHECK_END;
6753 if (!donteval) {
6754 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
6755 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
6756 if (y < 0 || y > 23) fatal("invalid y coordinate: %d", y);
6757 res->val = scrbase+6144+y*32+x;
6759 return expr;
6763 static const char *fnMArgToStr (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6764 const char *ee = expr;
6765 // argument is macro argument name
6766 expr = strSkipSpacesConst(expr);
6767 if (expr[0] != '=') { *error = UR_EXPRERR_FUNC; return ee; }
6768 ++expr;
6769 if (!isAlpha(expr[0]) && expr[0] != '_') { *error = UR_EXPRERR_FUNC; return ee; }
6770 const char *nend = expr+1;
6771 while (isAlphaDigit(*nend) || *nend == '_') ++nend;
6772 if (nend-expr > 127) { *error = UR_EXPRERR_FUNC; return ee; }
6773 char name[128];
6774 memset(name, 0, sizeof(name));
6775 memcpy(name, expr, nend-expr);
6776 expr = nend;
6777 // parse index
6778 int index = 0;
6779 int has_index = 0;
6780 expr = strSkipSpacesConst(expr);
6781 if (expr[0] == '[') {
6782 expr = strSkipSpacesConst(expr+1);
6783 if (expr[0] != ']') {
6784 has_index = 1;
6785 int doneg = 0;
6786 if (expr[0] == '+') ++expr;
6787 else if (expr[0] == '-') { doneg = 1; ++expr; }
6788 index = 0;
6789 while (isDigit(expr[0])) {
6790 index = index*10+(expr[0]-'0');
6791 if (index >= 0x1fffffff) fatal("`margtostr` index too high");
6792 ++expr;
6794 expr = strSkipSpacesConst(expr);
6795 if (doneg) index = -index;
6797 if (expr[0] != ']') fatal("`margtostr` invalid index syntax");
6798 ++expr;
6800 // done
6801 FN_CHECK_END;
6802 if (curmacro == NULL) fatal("`margtostr` outside of macro");
6803 if (!donteval) {
6804 for (int f = 0; f < curmacro->mac->argc; ++f) {
6805 if (strEquCI(name, curmacro->mac->argnames[f])) {
6806 // found argument, convert it to string
6807 if (!has_index) {
6808 res->str = realloc(res->str, strlen(curmacro->argvals[f])+1);
6809 strcpy(res->str, curmacro->argvals[f]);
6810 } else {
6811 const size_t slen = strlen(curmacro->argvals[f]);
6812 if (index < 0) {
6813 index = -index;
6814 if (index > slen) fatal("index out of string");
6815 index = (int)slen-index;
6817 if (index >= slen) fatal("index out of string");
6818 res->str = realloc(res->str, 2);
6819 res->str[0] = curmacro->argvals[f][index];
6820 res->str[1] = 0;
6822 // lowercase it
6823 for (char *s = res->str; *s; ++s) *s = toLower(*s);
6824 //fprintf(stderr, "<%s>\n", res->str);
6825 if (res->str[0]) {
6826 if (res->str[1]) {
6827 res->val = ((unsigned char)res->str[0]);
6828 res->val |= ((unsigned char)res->str[1])<<8;
6829 } else {
6830 res->val = (unsigned char)res->str[0];
6832 } else {
6833 res->val = 0;
6835 return expr;
6838 fatal("unknown macro argument '%s'", name);
6840 return expr;
6844 static const char *fnStrLen (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
6845 expr = strSkipSpacesConst(expr);
6846 int stlen = 0;
6847 switch (expr[0]) {
6848 case '"': case '\'': /* string literal? */
6850 char *a = (char *)expr+1;
6851 (void)parseStr(&a, expr[0], &stlen);
6852 expr = (const char *)a;
6854 break;
6855 default: /* expression */
6857 urasm_exprval_t r;
6858 urasm_exprval_init(&r);
6859 expr = urasm_expr_ex(&r, expr, disp, &donteval, defined, error);
6860 if (*error) fatalUrLib(*error);
6861 if (!r.str) fatal("string expected for `strlen()`");
6862 stlen = (int)strlen(r.str);
6863 urasm_exprval_clear(&r);
6865 break;
6867 FN_CHECK_END;
6868 if (!donteval) {
6869 urasm_exprval_setint(res, stlen);
6871 return expr;
6875 static void registerFunctions (void) {
6876 urasm_expr_register_func("defined", fnDefined);
6877 urasm_expr_register_func("known", fnKnown);
6878 urasm_expr_register_func("aligned256", fnAligned256);
6879 urasm_expr_register_func("align", fnAlign);
6880 urasm_expr_register_func("sameseg", fnSameSeg);
6881 urasm_expr_register_func("low", fnLow);
6882 urasm_expr_register_func("high", fnHigh);
6883 urasm_expr_register_func("abs", fnAbs);
6884 urasm_expr_register_func("bswap", fnBSwap);
6886 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8);
6887 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1);
6888 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8);
6890 urasm_expr_register_func("marg2str", fnMArgToStr);
6891 urasm_expr_register_func("strlen", fnStrLen);
6895 ///////////////////////////////////////////////////////////////////////////////
6896 // preparing another pass
6898 static void initPass (void) {
6899 asmMode = AMODE_NORMAL;
6900 currSrcLine = asmText;
6901 currModule = NULL;
6902 pc = start_pc;
6903 disp = start_disp;
6904 ent = start_ent;
6905 inTapeBlock = 0;
6906 tapeXorB = 0;
6907 wasOrg = 0;
6908 wasClr = 0;
6909 ifCount = 0;
6910 dfi_free();
6911 //defIncr = 0;
6912 lblOptMakeU2 = 0;
6913 curmacronum = 0;
6914 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
6915 lastSeenGlobalLabel = NULL;
6916 //lastSeenGlobalLabel = strdup(" [MAIN] ");
6917 modulesResetSeen();
6918 prepareMemory();
6919 urasm_use_old_priorities = 0;
6920 strcpy(lastFindLabelName, "");
6921 strcpy(currSeenLabel, "");
6922 urResetTempLabels();
6926 static int postPass (void) {
6927 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
6928 lastSeenGlobalLabel = NULL;
6929 if (checkLabels()) return -1;
6930 if (ifCount != 0) fatal("unbalanced IFs");
6931 return 0;
6935 ///////////////////////////////////////////////////////////////////////////////
6936 static int labelCmp (const void *aa, const void *bb) {
6937 if (aa == bb) return 0;
6938 const UrLabelInfo *a = *(const UrLabelInfo **)aa;
6939 const UrLabelInfo *b = *(const UrLabelInfo **)bb;
6940 return
6941 a->value < b->value ? -1 :
6942 a->value > b->value ? 1 :
6947 static __attribute__((unused)) int strStartsWith (const char *s, const char *pat) {
6948 if (!pat || !pat[0]) return 0;
6949 if (!s || !s[0]) return 0;
6950 while (*s && *pat) {
6951 if (*s != *pat) return 0;
6952 ++s;
6953 ++pat;
6955 return (*pat == 0);
6959 static int isGoodLabelForRef (const UrLabelInfo *ll) {
6960 if (!ll) return 0;
6963 // do not output `=` labels
6964 if (ll->type == LBL_TYPE_ASS) return 0;
6965 // do not output struct labels
6966 if (ll->type == LBL_TYPE_STOFFS) return 0;
6969 if (urasm_dump_all_labels) {
6970 if (strStartsWith(ll->name, "UFO_") || strStartsWith(ll->name, "USE_")) return 0;
6971 return 1;
6974 for (const char *s = ll->name; *s; ++s) {
6975 const char ch = *s;
6976 if (ch >= 'A' && ch <= 'Z') continue;
6977 if (ch >= '0' && ch <= '9') continue;
6978 if (ch == '_') continue;
6979 return 1;
6982 return 0;
6986 static void writeLabelsFile (const char *fname) {
6987 if (!fname || !fname[0]) return;
6989 // count labels
6990 int lcount = 0;
6991 for (const UrLabelInfo *ll = labels; ll; ll = ll->next) {
6992 if (isGoodLabelForRef(ll)) ++lcount;
6995 UrLabelInfo **larr = NULL;
6996 if (lcount > 0) {
6997 // create labels array
6998 larr = (UrLabelInfo **)malloc(sizeof(UrLabelInfo *)*lcount);
6999 lcount = 0;
7000 for (UrLabelInfo *ll = labels; ll; ll = ll->next) {
7001 if (isGoodLabelForRef(ll)) larr[lcount++] = ll;
7003 // sort labels
7004 qsort(larr, lcount, sizeof(UrLabelInfo *), &labelCmp);
7007 // write labels
7008 FILE *fo = fopen(fname, "w");
7009 if (lcount > 0) {
7010 for (int f = 0; f < lcount; ++f) {
7011 UrLabelInfo *ll = larr[f];
7012 //if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
7013 if (!ll->name) continue; // just in case
7014 if (ll->name[0] == '{') continue; // we don't have these, but it is still reserved
7015 if (ll->value < 0) {
7016 fprintf(fo, "%d %s", ll->value, ll->name);
7017 } else {
7018 fprintf(fo, "#%04X %s", (unsigned)ll->value, ll->name);
7020 fprintf(fo, " ");
7021 switch (ll->type) {
7022 case LBL_TYPE_VERY_SPECIAL: fprintf(fo, "WTF"); break;
7023 case LBL_TYPE_UNKNOWN: fprintf(fo, "UNKNOWN"); break;
7024 case LBL_TYPE_ASS: fprintf(fo, "assign"); break;
7025 case LBL_TYPE_EQU: fprintf(fo, "equ"); break;
7026 case LBL_TYPE_CODE: fprintf(fo, "code"); break;
7027 case LBL_TYPE_STOFS: fprintf(fo, "stofs"); break;
7028 case LBL_TYPE_DATA: fprintf(fo, "data"); break;
7029 default: fprintf(fo, "WTF"); break;
7031 fputc('\n', fo);
7033 free(larr);
7036 fclose(fo);
7040 ///////////////////////////////////////////////////////////////////////////////
7041 // options
7043 static struct option longOpts[] = {
7044 {"org", required_argument, NULL, 600},
7045 {"define", required_argument, NULL, 601},
7046 {"defzero", required_argument, NULL, 602},
7047 {"outdir", required_argument, NULL, 660},
7048 {"reffile", optional_argument, NULL, 669},
7049 {"outfile", required_argument, NULL, 665},
7050 {"castrates", optional_argument, NULL, 656},
7052 {"sna", 0, NULL, 's'},
7053 {"sna128", 0, NULL, 'S'},
7054 {"tap", 0, NULL, 't'},
7055 {"autotap", 0, NULL, 'T'},
7056 {"raw", 0, NULL, 'r'},
7057 {"autodmb", 0, NULL, 'B'},
7058 {"dmb", 0, NULL, 'b'},
7059 {"none", 0, NULL, 'n'},
7060 {"help", 0, NULL, 'h'},
7061 {"hob", 0, NULL, 'H'},
7062 {"scl", 0, NULL, 'l'},
7063 {"autoscl", 0, NULL, 'L'},
7064 {"monoscl", 0, NULL, 'm'},
7065 {"autosclmono", 0, NULL, 'M'},
7066 {"dsk", 0, NULL, 690},
7067 {"dsk720", 0, NULL, 691},
7068 {"adsk", 0, NULL, 692},
7069 {"adsk720", 0, NULL, 693},
7070 {"fixups", optional_argument, NULL, 'F'},
7072 {NULL, 0, NULL, 0}
7076 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
7079 static void usage (const char *pname) {
7080 printf(
7081 "usage: %s [options] infile\n"
7082 "default infiles:", pname);
7083 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
7084 printf("\n"
7085 "options:\n"
7086 " -s --sna write 48K .SNA file with autostart\n"
7087 " -S --sna128 write 148K .SNA file with autostart\n"
7088 " -t --tap write .tap file\n"
7089 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
7090 " -r --raw write raw file(s)\n"
7091 " -b --dmb write DMB file\n"
7092 " -B --autodmb write DMB file with autostart\n"
7093 " -H --hob write HoBeta code file(s)\n"
7094 " -l --scl write SCL TR-DOS archive\n"
7095 " -L --autoscl write autostarting SCL TR-DOS archive\n"
7096 " -m --monoscl write SCL with monoloader\n"
7097 /*" -M --monosclauto write autorun SCL with monoloader\n"*/
7098 " --dsk write +3DOS 180KB disk\n"
7099 " --dsk720 write +3DOS 720KB disk\n"
7100 " --adsk write +3DOS 180KB disk with autostart\n"
7101 " --adsk720 write +3DOS 720KB disk with autostart\n"
7102 " -n --none write nothing\n"
7103 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
7104 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
7105 " text: .txt file\n"
7106 " asm: address lists with counters\n"
7107 " asmdiff: 2nd and other addresses are relative, with counters\n"
7108 " asmz: address lists, with 0 end markers\n"
7109 " -h --help this help\n"
7110 "specials:\n"
7111 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
7112 " --org xxx set ORG\n"
7113 " --define val perform 'val EQU 1'\n"
7114 " --defzero val perform 'val EQU 0'\n"
7115 " --outdir dir output dir for resulting files (default: current)\n"
7116 " --outfile fname output file name for image formats (default: main asm file name)\n"
7117 " --castrates allow '&abc' hex numbers (and disable '&b' prefix)\n"
7122 ///////////////////////////////////////////////////////////////////////////////
7123 //#include "urforth.c"
7125 __attribute__((noreturn))
7126 void ufoFatalError (void) {
7127 longjmp(errJP, 666);
7130 uint32_t ufoZXGetU8 (uint32_t addr) {
7131 addr &= 0xffffU;
7132 return getByte(addr);
7135 void ufoZXPutU8 (uint32_t addr, uint32_t v) {
7136 addr &= 0xffffU;
7137 v &= 0xffU;
7138 putByte(addr, v);
7141 uint32_t ufoZXGetU16 (uint32_t addr) {
7142 addr &= 0xffffU;
7143 return getWord(addr);
7146 void ufoZXPutU16 (uint32_t addr, uint32_t v) {
7147 addr &= 0xffffU;
7148 v &= 0xffffU;
7149 putWord(addr, v);
7152 void ufoZXEmitU8 (uint32_t v) {
7153 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
7154 emitByte(v&0xffU);
7157 void ufoZXEmitU16 (uint32_t v) {
7158 //if (!zxlblLastByte) ufoFatal("label 'latest_byte' not found");
7159 emitWord(v&0xffffU);
7162 int ufoZXGetReserved (uint32_t addr) {
7163 return memresv[addr&0xffffU];
7166 void ufoZXSetReserved (uint32_t addr, int resvflag) {
7167 memresv[addr&0xffffU] = resvflag;
7170 int ufoZXGetPass (void) {
7171 return pass;
7174 uint32_t ufoZXGetOrg (void) {
7175 return pc;
7178 void ufoZXSetOrg (uint32_t addr) {
7179 if (addr > 0xffff) ufoFatal("invalid ORG address: %u", addr);
7180 pc = disp = (uint16_t)addr;
7181 if (!wasOrg) {
7182 wasOrg = 1; // so next `ORG` will not reset it
7183 ent = (uint16_t)addr;
7187 uint32_t ufoZXGetDisp (void) {
7188 return disp;
7191 void ufoZXSetDisp (uint32_t addr) {
7192 if (addr > 0xffff) ufoFatal("invalid DISP address: %u", addr);
7193 disp = (uint16_t)addr;
7196 uint32_t ufoZXGetEnt (void) {
7197 return (wasOrg ? (uint32_t)ent : ~0u);
7200 void ufoZXSetEnt (uint32_t addr) {
7201 if (addr > 0xffff) ufoFatal("invalid ENT address: %u", addr);
7202 wasOrg = 1; // so next `ORG` will not reset it
7203 ent = (uint16_t)addr;
7206 static int ufoZXLType2UFO (int type) {
7207 switch (type) {
7208 case LBL_TYPE_VERY_SPECIAL: return UFO_ZX_LABEL_UNDEFINED;
7209 case LBL_TYPE_UNKNOWN: return UFO_ZX_LABEL_UNKNOWN;
7210 case LBL_TYPE_ASS: return UFO_ZX_LABEL_VAR;
7211 case LBL_TYPE_EQU: return UFO_ZX_LABEL_EQU;
7212 case LBL_TYPE_CODE: return UFO_ZX_LABEL_CODE;
7213 case LBL_TYPE_DATA: return UFO_ZX_LABEL_DATA;
7214 case LBL_TYPE_STOFS: return UFO_ZX_LABEL_STOFS;
7216 ufoFatal("WTF?! unknown label type %d", type);
7219 int ufoZXGetLabelType (const char *name) {
7220 if (name == NULL || name[0] == 0) return UFO_ZX_LABEL_UNDEFINED;
7221 UrLabelInfo *lbl = findAddLabel(name);
7222 if (lbl == NULL) return UFO_ZX_LABEL_UNDEFINED;
7223 return ufoZXLType2UFO(lbl->type);
7226 int ufoZXGetLabelValue (const char *name) {
7227 if (name == NULL || name[0] == 0) return 0;
7228 UrLabelInfo *lbl = findAddLabel(name);
7229 return (lbl ? lbl->value : 0);
7232 // this also creates labels
7233 void ufoZXSetLabelValue (const char *name, int type, int value) {
7234 if (name == NULL || name[0] == 0) ufoFatal("empty label name");
7236 // convert label type
7237 switch (type) {
7238 case UFO_ZX_LABEL_UNDEFINED: ufoFatal("cannot set label with undefined type");
7239 case UFO_ZX_LABEL_UNKNOWN: ufoFatal("cannot set label with unknown type");
7240 case UFO_ZX_LABEL_VAR: type = LBL_TYPE_ASS; break;
7241 case UFO_ZX_LABEL_EQU: type = LBL_TYPE_EQU; break;
7242 case UFO_ZX_LABEL_CODE: type = LBL_TYPE_CODE; break;
7243 case UFO_ZX_LABEL_DATA: type = LBL_TYPE_DATA; break;
7244 case UFO_ZX_LABEL_STOFS: type = LBL_TYPE_STOFS; break;
7245 default: ufoFatal("invalid label type %d", type);
7248 UrLabelInfo *lbl = findAddLabel(name);
7250 if (lbl->type != LBL_TYPE_UNKNOWN && lbl->type != type) {
7251 ufoFatal("invalid label '%s' type", name);
7253 if (type != LBL_TYPE_ASS) {
7254 if (lbl->type >= 0 && lbl->value != value) ufoFatal("invalid label '%s' value", name);
7257 lbl->value = value;
7258 if (lbl->type == LBL_TYPE_UNKNOWN) lbl->type = type;
7261 #define UFO_ZX_MAX_ITERS (16)
7263 typedef struct {
7264 UFOZXLabelIterator id;
7265 UrLabelInfo *lbl;
7266 } UZXLabelIter;
7268 static UZXLabelIter ufoZXLblIters[UFO_ZX_MAX_ITERS];
7269 static uint32_t ufoZXLblIdLast;
7271 static int UFOZXFindIter (UFOZXLabelIterator it) {
7272 int f = 0;
7273 while (f != UFO_ZX_MAX_ITERS && ufoZXLblIters[f].id != it) f += 1;
7274 if (f == UFO_ZX_MAX_ITERS) f = -1;
7275 return f;
7278 static UrLabelInfo *UFOZXNormLabel (UrLabelInfo *c) {
7279 while (c != NULL && (c->type == LBL_TYPE_VERY_SPECIAL || c->type == LBL_TYPE_UNKNOWN)) {
7280 c = c->next;
7282 return c;
7285 static void UFOZXLabelRemoved (UrLabelInfo *lbl) {
7286 ur_assert(lbl != NULL);
7287 UrLabelInfo *c = UFOZXNormLabel(lbl->next);
7288 for (int f = 0; f < UFO_ZX_MAX_ITERS; f += 1) {
7289 if (ufoZXLblIters[f].lbl == lbl) {
7290 ufoZXLblIters[f].lbl = c;
7295 UFOZXLabelIterator ufoZXNewLabelIter (void) {
7296 UFOZXLabelIterator res = 0;
7297 UrLabelInfo *c = UFOZXNormLabel(labels);
7298 if (c != NULL) {
7299 int f = 0;
7300 while (res == 0 && f != UFO_ZX_MAX_ITERS) {
7301 if (ufoZXLblIters[f].id == 0) {
7302 ufoZXLblIdLast += 1;
7303 if (ufoZXLblIdLast == 0) ufoZXLblIdLast = 1;
7304 ufoZXLblIters[f].id = ufoZXLblIdLast;
7305 ufoZXLblIters[f].lbl = c;
7306 res = ufoZXLblIdLast;
7310 return res;
7313 int ufoZXLabelIterNext (UFOZXLabelIterator it) {
7314 int res = 0;
7315 int f = UFOZXFindIter(it);
7316 if (f != -1) {
7317 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7318 if (c != NULL) c = UFOZXNormLabel(c->next);
7319 if (c != NULL) {
7320 ufoZXLblIters[f].lbl = c;
7321 res = 1;
7322 } else {
7323 ufoZXLblIters[f].id = 0;
7324 ufoZXLblIters[f].lbl = NULL;
7327 return res;
7330 void ufoZXLabelIterClose (UFOZXLabelIterator it) {
7331 int f = UFOZXFindIter(it);
7332 if (f != -1) {
7333 ufoZXLblIters[f].id = 0;
7334 ufoZXLblIters[f].lbl = NULL;
7338 const char *ufoZXLabelIterGetName (UFOZXLabelIterator it) {
7339 int f = UFOZXFindIter(it);
7340 if (f != -1) {
7341 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7342 if (c != NULL) return c->name;
7344 return NULL;
7347 int ufoZXIterGetValue (UFOZXLabelIterator it) {
7348 int f = UFOZXFindIter(it);
7349 if (f != -1) {
7350 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7351 if (c != NULL) return c->value;
7353 return 0;
7356 int ufoZXIterGetType (UFOZXLabelIterator it) {
7357 int f = UFOZXFindIter(it);
7358 if (f != -1) {
7359 UrLabelInfo *c = ufoZXLblIters[f].lbl;
7360 if (c != NULL) ufoZXLType2UFO(c->type);
7362 return UFO_ZX_LABEL_UNDEFINED;
7365 const char *ufoGetSrcLine (const char **fname, int *lnum) {
7366 SourceLine *sl = nextUFOSrcLine();
7367 if (sl == NULL) return NULL;
7368 *lnum = sl->lineNo;
7369 *fname = sl->fname;
7370 return (sl->line != NULL ? sl->line : "");
7373 char *ufoCreateIncludeName (const char *fname, int assystem, const char *lastIncPath) {
7374 assystem = (assystem ? -666 : -669);
7375 if (lastIncPath == NULL) lastIncPath = ufoIncludeDir;
7376 return createIncludeName(fname, assystem, NULL, lastIncPath);
7379 void ufoZXPostInit (void) {
7380 for (int f = 0; f < UFO_ZX_MAX_ITERS; f += 1) {
7381 ufoZXLblIters[f].id = 0;
7382 ufoZXLblIters[f].lbl = NULL;
7384 ufoZXLblIdLast = 0;
7388 ///////////////////////////////////////////////////////////////////////////////
7389 // main
7391 int main (int argc, char *argv[]) {
7392 int res = 0, c;
7393 const char *pname = argv[0];
7394 char *inFile = NULL;
7395 char **defines = NULL, **values = NULL;
7396 int defcount = 0;
7397 int wantref = 0;
7399 initLabelBuckets();
7400 modulesClear();
7401 initInclideDir();
7402 initUFEInclideDir();
7404 urasm_getbyte = getByte;
7405 urasm_putbyte = putByte;
7406 urasm_label_by_name = findLabelCB;
7407 urasm_getval = getValueCB;
7408 urasm_expand = expandCB;
7409 urasm_fixup_operand = fixupOperandCB;
7411 //strcpy(tapeLoaderName, "cargador ");
7412 tapeLoaderName[0] = 0;
7414 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
7415 while ((c = getopt_long(argc, argv, "sStTrBbnhHFLlMm", longOpts, NULL)) >= 0) {
7416 switch (c) {
7417 case '?': return 1;
7418 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
7419 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
7420 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
7421 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
7422 case 'L': optRunSCL = 1; optWriteType = 'S'; optWTChanged = 1; break; /* with boot */
7423 case 'l': optRunSCL = -1; optWriteType = 'S'; optWTChanged = 1; break; /* no boot */
7424 case 'm': optRunSCL = -1; optWriteType = 'M'; optWTChanged = 1; break; /* no boot */
7425 case 'M': optRunSCL = 1; optWriteType = 'M'; optWTChanged = 1; break; /* with boot */
7426 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
7427 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
7428 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
7429 case 'h': usage(pname); res = 0; goto earlyerrquit;
7430 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
7431 case 'F':
7432 optWriteFixups = 1;
7433 if (optarg != NULL) {
7434 if (strcmp(optarg, "asm") == 0 || strcmp(optarg, "zas") == 0) {
7435 optFixupType = 1;
7436 } else if (strcmp(optarg, "asmdiff") == 0 || strcmp(optarg, "zasdiff") == 0) {
7437 optFixupType = 2;
7438 } else if (strcmp(optarg, "asmz") == 0 || strcmp(optarg, "zasz") == 0) {
7439 optFixupType = 3;
7440 } else if (strcmp(optarg, "text") == 0) {
7441 optFixupType = 0;
7442 } else {
7443 fprintf(stderr, "FATAL: invalid fixup type: '%s'\n", optarg);
7444 return 1;
7447 break;
7448 case 600: // org
7449 c = atoi(optarg);
7450 //fprintf(stderr, "ORG: %d\n", c);
7451 if (c < 0 || c > 65535) {
7452 fprintf(stderr, "FATAL: invalid ORG: %d\n", c);
7453 return 1;
7455 start_pc = start_disp = start_ent = c;
7456 break;
7457 case 601: // define
7458 //fprintf(stderr, "define: [%s]\n", optarg);
7459 defines = realloc(defines, sizeof(char *)*(defcount+1));
7460 values = realloc(values, sizeof(char *)*(defcount+1));
7461 defines[defcount] = strdup(optarg);
7462 values[defcount] = strdup("1");
7463 ++defcount;
7464 break;
7465 case 602: // defzero
7466 //fprintf(stderr, "defzero: [%s]\n", optarg);
7467 defines = realloc(defines, sizeof(char *)*(defcount+1));
7468 values = realloc(values, sizeof(char *)*(defcount+1));
7469 defines[defcount] = strdup(optarg);
7470 values[defcount] = strdup("0");
7471 ++defcount;
7472 break;
7473 case 660: // outdir
7474 if (optOutputDir != NULL) free(optOutputDir);
7475 optOutputDir = strdup(optarg);
7476 break;
7477 case 665: // outdir
7478 if (optOutputFile != NULL) free(optOutputFile);
7479 optOutputFile = strdup(optarg);
7480 break;
7481 case 669: // reffile
7482 if (refFileName) free(refFileName);
7483 refFileName = (optarg ? strdup(optarg) : NULL);
7484 wantref = 1;
7485 break;
7486 case 690: case 692: // +3DOS 180K disk
7487 optRunDSK = (c == 692);
7488 optDskType = P3DSK_PCW_SS;
7489 optWriteType = '3';
7490 optWTChanged = 1;
7491 break;
7492 case 691: case 693: // +3DOS 720K disk
7493 optRunDSK = (c == 693);
7494 optDskType = P3DSK_PCW_DS;
7495 optWriteType = '3';
7496 optWTChanged = 1;
7497 break;
7498 case 656: // hex castrates
7499 if (optarg != NULL) {
7500 if (strcmp(optarg, "tan") == 0 || strcmp(optarg, "yes") == 0) {
7501 urasm_allow_hex_castrates = 1;
7502 } else if (strcmp(optarg, "ona") == 0 || strcmp(optarg, "no") == 0) {
7503 urasm_allow_hex_castrates = 0;
7504 } else {
7505 fprintf(stderr, "FATAL: i don't know what '%s' means.\n", optarg);
7506 return 1;
7508 } else {
7509 urasm_allow_hex_castrates = 1;
7511 break;
7515 if (optind >= argc) {
7516 // try to find default input file
7517 for (int f = 0; defInFiles[f]; ++f) {
7518 if (!access(defInFiles[f], R_OK)) {
7519 inFile = strdup(defInFiles[f]);
7520 break;
7523 } else {
7524 inFile = strdup(argv[optind]);
7527 if (!inFile || !inFile[0]) {
7528 res = 1;
7529 fprintf(stderr, "ERROR: no input file!\n");
7530 goto earlyerrquit;
7533 if (optOutputDir == NULL) optOutputDir = strdup(".");
7534 #ifdef WIN32
7535 for (char *ss = optOutputDir; *ss; ++ss) if (*ss == '\\') *ss = '/';
7536 #endif
7538 char *ee = strrchr(optOutputDir, '/');
7539 if (ee && ee != optOutputDir) *ee = 0;
7541 if (!optOutputDir[0]) { free(optOutputDir); optOutputDir = strdup("."); }
7543 registerInstructions();
7544 registerFunctions();
7546 res = asmTextLoad(inFile, 0);
7547 if (!res) {
7548 for (int f = 0; f < defcount; ++f) {
7549 if (labelDoEQU(defines[f], values[f]) != 0) {
7550 fprintf(stderr, "FATAL: can't define label: '%s'\n", defines[f]);
7551 goto errquit;
7553 //fprintf(stderr, "****SET**** <%s> to <%s>\n", defines[f], values[f]);
7556 for (pass = 0; pass <= 1; ++pass) {
7557 initPass();
7558 printf("pass %d\n", pass);
7559 //fprintf(stderr, "===========pass %d===========\n", pass);
7560 setCurSrcLine(asmText);
7561 if (setjmp(errJP)) { res = 1; break; }
7562 while (currSrcLine) processCurrentLine();
7563 if (postPass()) { res = 1; break; }
7566 // write result
7567 if (res == 0) {
7568 char *oc = strdup(inFile);
7569 char *pd = strrchr(oc, '.');
7570 #ifdef WIN32
7571 if (pd && !strchr(oc, '/') && !strchr(oc, '\\')) *pd = '\0';
7572 #else
7573 if (pd && !strchr(oc, '/')) *pd = '\0';
7574 #endif
7575 switch (optWriteType) {
7576 case 's': saveSna(oc, optSNA48); break;
7577 case 't': saveTap(oc); break;
7578 case 'r': saveRaw(oc); break;
7579 case 'd': saveDMB(oc); break;
7580 case 'H': saveHob(oc); break;
7581 case 'S': saveSCL(oc); break;
7582 case 'M': saveSCLMono(oc); break;
7583 case '3': saveDSK(oc); break;
7585 free(oc);
7586 if (optWriteFixups) writeFixups();
7587 if (wantref) {
7588 /* build ref file name */
7589 if (!refFileName || !refFileName[0]) {
7590 if (refFileName) free(refFileName);
7591 refFileName = malloc(strlen(inFile)+128);
7592 strcpy(refFileName, inFile);
7593 char *ext = strrchr(refFileName, '.');
7594 if (!ext) {
7595 strcat(refFileName, ".ref");
7596 } else {
7597 #ifdef WIN32
7598 char *slash = NULL;
7599 for (char *ts = refFileName; *ts; ++ts) {
7600 if (*ts == '/' || *ts == '\\') slash = ts;
7602 #else
7603 char *slash = strrchr(refFileName, '/');
7604 #endif
7605 if (!slash || slash < ext) {
7606 strcpy(ext, ".ref");
7607 } else {
7608 strcat(refFileName, ".ref");
7612 writeLabelsFile(refFileName);
7613 printf("refs written to '%s'\n", refFileName);
7614 free(refFileName);
7617 } else {
7618 fprintf(stderr, "ERROR: loading error!\n");
7621 errquit:
7622 ufoDeinit();
7623 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
7624 clearFixups();
7625 urClearLabels();
7626 modulesClear();
7627 asmTextClear();
7628 urClearOps();
7629 clearMacros();
7630 freeStructs();
7631 clearKnownFiles();
7633 earlyerrquit:
7634 if (inFile) free(inFile);
7635 if (sysIncludeDir) free(sysIncludeDir);
7636 if (ufoIncludeDir) free(ufoIncludeDir);
7637 if (lastIncludePath) free(lastIncludePath);
7638 if (lastSysIncludePath) free(lastSysIncludePath);
7639 for (int f = defcount-1; f >= 0; --f) {
7640 free(values[f]);
7641 free(defines[f]);
7643 if (defines != NULL) { free(values); free(defines); }
7644 if (optOutputFile) free(optOutputFile);
7645 if (optOutputDir) free(optOutputDir);
7647 return (res ? 1 : 0);