dsforth: better decompiler
[urasm.git] / src / urasm.c
blob6e5bcf6c4a0d3c2e3e6cc70a1980ef6b7e582277
1 // URASM Z80 assembler
2 // coded by Ketmar // Invisible Vector
3 // GPLv3 or later
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"
29 #include "ursna48.c"
32 #define VERSION_HI 0
33 #define VERSION_MID 2
34 #define VERSION_LO 2
37 #define MAYBE_UNUSED __attribute__((unused))
39 #define lambda(return_type, body_and_args) ({ \
40 return_type __fn__ body_and_args \
41 __fn__; \
45 ////////////////////////////////////////////////////////////////////////////////
46 static inline int isSpace (char ch) { return (ch && ((unsigned)(ch&0xff) <= 32 || ch == 127)); }
47 static inline int isAlpha (char ch) { return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')); }
48 static inline int isDigit (char ch) { return (ch >= '0' && ch <= '9'); }
49 static inline int isHexDigit (char ch) { return ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')); }
50 static inline int isAlphaDigit (char ch) { return (isAlpha(ch) || isDigit(ch)); }
52 static inline char toUpper (char ch) { return (ch >= 'a' && ch <= 'z' ? ch-'a'+'A' : ch); }
53 static inline char toLower (char ch) { return (ch >= 'A' && ch <= 'Z' ? ch-'A'+'a' : ch); }
55 static int digitInBase (char ch, int base) {
56 if (ch < '0') return -1;
57 if (base <= 10) {
58 if (ch >= '0'+base) return -1;
59 return ch-'0';
61 ch = toUpper(ch);
62 if (ch <= '9') return ch-'0';
63 if (ch < 'A' || ch > 'A'+base-10) return -1;
64 ch -= 'A'-10;
65 return (ch < base ? ch : -1);
69 ////////////////////////////////////////////////////////////////////////////////
70 static char *strprintfVA (const char *fmt, va_list vaorig) {
71 char *buf = NULL;
72 int olen, len = 128;
74 buf = malloc(len);
75 if (buf == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
76 for (;;) {
77 char *nb;
78 va_list va;
80 va_copy(va, vaorig);
81 olen = vsnprintf(buf, len, fmt, va);
82 va_end(va);
83 if (olen >= 0 && olen < len) return buf;
84 if (olen < 0) olen = len*2-1;
85 nb = realloc(buf, olen+1);
86 if (nb == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
87 buf = nb;
88 len = olen+1;
93 static __attribute((format(printf,1,2))) char *strprintf (const char *fmt, ...) {
94 char *buf = NULL;
95 va_list va;
97 va_start(va, fmt);
98 buf = strprintfVA(fmt, va);
99 va_end(va);
100 return buf;
104 ///////////////////////////////////////////////////////////////////////////////
105 // global variables
107 enum {
108 AMODE_NORMAL = 0,
109 AMODE_FCODE = 1, // forth assembler code word
110 AMODE_FWORD = 2, // forth threaded code word
113 static int asmMode = 0;
115 static char *sysIncludeDir = NULL;
116 static char *refFileName = NULL;
117 static char *lastIncludePath = NULL;
118 static char *lastSysIncludePath = NULL;
120 #define MAX_LINE_SIZE 16384
121 static char currLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
124 ///////////////////////////////////////////////////////////////////////////////
125 // init dirs
127 static void initInclideDir (void) {
128 const char *id = getenv("URASM_INCLUDE_DIR");
129 if (id && id[0]) {
130 sysIncludeDir = strdup(id);
131 } else {
132 char myDir[4096];
133 memset(myDir, 0, sizeof(myDir));
134 #ifndef WIN32
135 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
136 strcpy(myDir, ".");
137 } else {
138 char *p = (char *)strrchr(myDir, '/');
139 if (!p) strcpy(myDir, "."); else *p = '\0';
141 #else
142 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
143 char *p = strrchr(myDir, '\\');
144 if (!p) strcpy(myDir, "."); else *p = '\0';
145 #endif
146 strcat(myDir, "/libs");
147 sysIncludeDir = strdup(myDir);
149 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
150 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
154 ///////////////////////////////////////////////////////////////////////////////
155 // string utilities
157 /* trim trailing spaces and comments; normalize colons */
158 static void normalizeForthStr (char *s) {
159 char *p = s;
160 /* now skip all shit */
161 while (*p) {
162 const char ch = *p++;
163 /* comment */
164 if (ch == ';' && p[0] == ';') { p[-1] = 0; break; }
166 /* done; trim trailing spaces and colons */
167 size_t slen = strlen(s);
168 while (slen > 0 && isSpace(s[slen-1])) --slen;
169 s[slen] = 0;
173 /* trim trailing spaces and comments; normalize colons */
174 static void normalizeStr (char *s) {
175 if (asmMode == AMODE_FWORD) return normalizeForthStr(s);
176 if (strcasestr(s, "$FORTH")) return normalizeForthStr(s);
177 char *p = s;
178 /* now skip all shit */
179 while (*p) {
180 const char ch = *p++;
181 /* check for "af'" */
182 if (ch == '\'' && p-s >= 2 && toLower(p[-2] == 'a') && toLower(p[-1] == 'f')) continue;
183 /* comment */
184 if (ch == ';') { p[-1] = 0; break; }
185 /* string */
186 if (ch == '"' || ch == '\'') {
187 const char qch = ch;
188 while (*p) {
189 const char c1 = *p++;
190 if (c1 == qch) break;
191 if (c1 == '\\' && *p) ++p;
193 continue;
195 /* reduce and normalise colons */
196 if (ch == ':') {
197 --p; /* back to colon */
198 /* remove spaces before colon */
199 char *t = p;
200 while (t != s && isSpace(t[-1])) --t;
201 if (t != p) memmove(t, p, strlen(p)+1);
202 p = t;
203 if (p[0] != ':') abort(); // assert
204 ++p; /* skip colon */
205 /* remove following spaces and colons */
206 t = p;
207 while (*t == ':' || isSpace(*t)) ++t;
208 if (t != p) memmove(p, t, strlen(t)+1);
209 continue;
212 /* done; trim trailing spaces and colons */
213 size_t slen = strlen(s);
214 while (slen > 0 && (isSpace(s[slen-1]) || s[slen-1] == ':')) --slen;
215 s[slen] = 0;
219 /* check if string starts with the given command (case-insensitive) */
220 /* returns NULL or pointer to args */
221 /* skips spaces after command if any */
222 static char *strIsCommand (const char *command, char *str) {
223 for (int cnt = 1; cnt > 0; --cnt) {
224 while (*str && isSpace(*str)) ++str; // skip spaces
225 for (; *command && *str; ++command, ++str) {
226 if (toUpper(*command) != toUpper(*str)) return NULL; // alas
228 if (*command) return NULL; // alas
229 if (*str && isAlphaDigit(*str)) return NULL; // alas
230 while (*str && isSpace(*str)) ++str; // skip spaces
231 if (*str && *str == ':') break; // try again if we have a colon
232 return str; // found
234 return NULL;
238 /* parse string literal */
239 /* don't free() result */
240 /* skips trailing spaces */
241 static char *parseStr (char **str, char endQ, int *lenp) {
242 static char buf[MAX_LINE_SIZE];
243 int len = 0, n, f, base;
244 char *a = *str;
245 memset(buf, 0, sizeof(buf));
246 if (lenp) *lenp = 0;
247 for (; *a; ++a) {
248 if (*a == '\\') {
249 if (!a[1]) break;
250 switch (*(++a)) {
251 case 'a': buf[len++] = '\a'; break;
252 case 'b': buf[len++] = '\b'; break;
253 case 'e': buf[len++] = '\x1b'; break;
254 case 'f': buf[len++] = '\f'; break;
255 case 'n': buf[len++] = '\n'; break;
256 case 'r': buf[len++] = '\r'; break;
257 case 't': buf[len++] = '\t'; break;
258 case 'v': buf[len++] = '\v'; break;
259 case 'z': buf[len++] = '\0'; break;
260 case 'x': case 'X': // hex
261 ++a; // skip 'x'
262 base = 16; f = 2;
263 donum: for (n = 0; f > 0; --f) {
264 char ch = digitInBase(*a++, base);
266 if (ch < 0) { --a; break; }
267 n *= base;
268 n += ch;
270 buf[len++] = n;
271 --a; // return to the last digit, 'for' will skip it
272 break;
273 case '0': // octal
274 base = 8; f = 4;
275 goto donum;
276 case '1' ... '9': // decimal
277 base = 10; f = 3;
278 goto donum;
279 default: buf[len++] = a[0]; break; // others
281 } else {
282 if (*a == endQ) { ++a; break; }
283 buf[len++] = *a;
286 while (*a && isSpace(*a)) ++a; // skip trailing spaces
287 *str = a;
288 buf[len] = '\0';
289 if (lenp) *lenp = len;
290 return buf;
294 ///////////////////////////////////////////////////////////////////////////////
295 // source file stack, reader, etc
297 typedef struct SourceLine {
298 struct SourceLine *next;
299 char *line;
300 char *fname;
301 int lineNo;
302 int system;
303 } SourceLine;
305 static SourceLine *asmText = NULL;
306 static SourceLine *asmTextLast = NULL;
307 static SourceLine *curSrcLine = NULL;
309 #define MAX_MACRO_ARGS (32)
311 typedef struct MacroDef {
312 struct MacroDef *next;
313 char *name;
314 SourceLine *lines;
315 int argc;
316 char *argdefaults[MAX_MACRO_ARGS]; // default values
317 char *argnames[MAX_MACRO_ARGS]; // argument names
318 } MacroDef;
320 typedef struct {
321 MacroDef *mac;
322 char *argvals[MAX_MACRO_ARGS]; // argument values
323 } CurMacroDef;
325 static MacroDef *maclist = NULL;
326 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
327 static int curmacronum = 0;
330 static MacroDef *findMacro (const char *name) {
331 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strcasecmp(name, mc->name) == 0) return mc;
332 return NULL;
336 static void asmTextClear (void) {
337 while (asmText) {
338 SourceLine *l = asmText;
340 asmText = asmText->next;
341 free(l->line);
342 free(l->fname);
343 free(l);
345 asmTextLast = curSrcLine = NULL;
349 static int asmTextLoad (const char *fname, int system) {
350 FILE *fl;
351 int lineNo = 0;
352 SourceLine *s;
354 if (!(fl = fopen(fname, "r"))) {
355 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
356 return -1;
358 printf("loading: %s\n", fname);
359 // read file
360 while (fgets(currLine, sizeof(currLine)-1, fl)) {
361 ++lineNo;
362 currLine[sizeof(currLine)-1] = '\0';
363 //!normalizeStr(currLine);
364 //fprintf(stderr, "*[%s]\n", curLine);
365 if (!currLine[0]) continue; // don't store empty lines
366 // add current line
367 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
368 s->lineNo = lineNo;
369 s->system = system;
370 if ((s->line = strdup(currLine)) == NULL) abort();
371 if ((s->fname = strdup(fname)) == NULL) abort();
372 if (asmTextLast) asmTextLast->next = s; else asmText = s;
373 asmTextLast = s;
375 fclose(fl);
376 return 0;
380 static char *extractFileDir (const char *s) {
381 if (!s || !s[0]) return strdup("");
382 const char *slash;
383 #ifdef WIN32
384 slash = NULL;
385 for (const char *ts = s; *ts; ++ts) {
386 if (*ts == '/' || *ts == '\\') slash = ts;
388 if (slash == s && (s[0] == '/' || s[0] == '\\') && (s[1] == '/' || s[1] == '\\')) slash = NULL;
389 #else
390 slash = strrchr(s, '/');
391 #endif
392 if (!slash) return strdup("");
393 ptrdiff_t len = (ptrdiff_t)(slash-s)+1;
394 char *res = malloc(len+1);
395 memcpy(res, s, len);
396 res[len] = 0;
397 #ifdef WIN32
398 while (len > 0 && (res[len-1] == '\\' || res[len-1] == '/')) --len;
399 #else
400 while (len > 0 && res[len-1] == '/') --len;
401 #endif
402 if (len == 0) { free(res); return strdup(""); }
403 res[len] = 0;
404 return res;
408 static void loadCurSrcLine (void) {
409 if (curSrcLine) {
410 strcpy(currLine, curSrcLine->line);
411 /* macros will not change include dirs */
412 if (!curmacro) {
413 char **incpp = (!curSrcLine->system ? &lastIncludePath : &lastSysIncludePath);
414 if (*incpp) free(*incpp);
415 *incpp = extractFileDir(curSrcLine->fname);
417 normalizeStr(currLine);
418 } else {
419 currLine[0] = 0;
423 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
425 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
428 static inline int strHasPathDelim (const char *s) {
429 if (!s || !s[0]) return 0;
430 #ifdef WIN32
431 return (strchr(s, '/') || strchr(s, '\\') ? 1 : 0);
432 #else
433 return (strchr(s, '/') ? 1 : 0);
434 #endif
438 /* returns malloced string */
439 static char *createIncludeName (const char *fname, int assystem, const char *defaultmain) {
440 if (!fname || !fname[0]) return NULL;
441 char *res;
442 if (fname[0] != '/') {
443 const char *incdir;
444 if (!assystem) {
445 incdir = lastIncludePath;
446 } else {
447 incdir = lastSysIncludePath;
448 if (!incdir || !incdir[0]) incdir = sysIncludeDir;
450 res = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
451 } else {
452 res = strprintf("%s", fname);
454 struct stat st;
455 if (defaultmain && defaultmain[0]) {
456 if (stat(res, &st) == 0) {
457 if (S_ISDIR(st.st_mode)) {
458 char *rs = strprintf("%s/%s", res, defaultmain);
459 free(res);
460 res = rs;
464 /* check if there is disk file */
465 if (strHasPathDelim(fname) && stat(res, &st) != 0) {
466 /* no file, try "root include" */
467 const char *incdir = (!assystem ? NULL : sysIncludeDir);
468 char *rs = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
469 free(res);
470 res = rs;
471 /* check for dir again */
472 if (defaultmain && defaultmain[0]) {
473 if (stat(res, &st) == 0) {
474 if (S_ISDIR(st.st_mode)) {
475 char *rs = strprintf("%s/%s", res, defaultmain);
476 free(res);
477 res = rs;
482 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
483 return res;
487 static int includeCount = 0;
489 // process 'INCLUDE'
490 // include file instead of the current line
491 static int asmTextInclude (const char *fname, int system) {
492 char *fn;
493 FILE *fl;
494 int lineNo = 0;
495 SourceLine *first = NULL, *last = NULL, *s = NULL;
497 if (includeCount > 256) {
498 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine->fname, curSrcLine->lineNo);
499 return -1;
502 fn = createIncludeName(fname, system, "zzmain.zas");
503 ++includeCount;
504 printf("loading: %s\n", fn);
505 if ((fl = fopen(fn, "r")) == NULL) {
506 fprintf(stderr, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine->fname, curSrcLine->lineNo, currLine);
507 free(fn);
508 return -1;
511 while (fgets(currLine, sizeof(currLine)-1, fl)) {
512 ++lineNo;
513 currLine[sizeof(currLine)-1] = '\0';
514 const size_t slen = strlen(currLine);
515 if (slen == 0 || (currLine[slen-1] != '\n' && currLine[slen-1] != '\r')) {
516 fprintf(stderr, "ERROR: file %s, line %d: line too long\n", fn, lineNo);
517 free(fn);
518 return -1;
520 //!normalizeStr(currLine);
521 if (!currLine[0]) continue; // don't store empty lines
522 // add current line
523 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
524 s->system = system;
525 s->lineNo = lineNo;
526 if ((s->line = strdup(currLine)) == NULL) abort();
527 if ((s->fname = strdup(fn)) == NULL) abort();
528 if (last != NULL) last->next = s; else first = s;
529 last = s;
531 fclose(fl);
532 free(fn);
533 --includeCount;
534 curSrcLine->line[0] = 0;
535 if (last) {
536 last->next = curSrcLine->next;
537 curSrcLine->next = first;
539 return 0;
543 ///////////////////////////////////////////////////////////////////////////////
544 // prototypes
546 static void processCurrentLine (void); // only one, will skip to next one
547 static void processForthWordLine (void); // only one, will skip to next one
550 ///////////////////////////////////////////////////////////////////////////////
551 // error raisers, etc
553 static jmp_buf errJP;
556 static void errorWriteFile (FILE *fo) {
557 if (curSrcLine) {
558 fprintf(fo, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
559 } else {
560 fprintf(fo, "somewhere in time: ");
564 static void errorMsgV (const char *fmt, va_list ap) {
565 errorWriteFile(stderr);
566 vfprintf(stderr, fmt, ap);
567 va_end(ap);
568 fputc('\n', stderr);
569 fflush(stderr);
573 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
574 va_list ap;
575 fprintf(stderr, "WARNING ");
576 va_start(ap, fmt);
577 errorMsgV(fmt, ap);
581 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
582 va_list ap;
583 fprintf(stderr, "FATAL ");
584 va_start(ap, fmt);
585 errorMsgV(fmt, ap);
589 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
590 va_list ap;
591 va_start(ap, fmt);
592 errorMsgV(fmt, ap);
593 longjmp(errJP, 666);
597 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
598 errorMsg("%s", urasm_errormsg(errcode));
599 longjmp(errJP, 666);
603 //////////////////////////////////////////////////////////////////////////////
604 // operator management
606 // return !0 to skip current line
607 typedef int (*UrAsmOpFn) (void);
609 enum {
610 PI_CONT_LINE = 0,
611 PI_SKIP_LINE = 1
614 typedef struct UrAsmOp {
615 char *name;
616 UrAsmOpFn fn;
617 struct UrAsmOp *next;
618 } UrAsmOp;
620 static UrAsmOp *oplist = NULL;
623 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
624 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
625 if (!res) abort();
626 res->name = strdup(name);
627 res->fn = fn;
628 res->next = oplist;
629 oplist = res;
630 return res;
634 static UrAsmOp *urFindOp (const char *name) {
635 UrAsmOp *res;
636 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
637 return res;
641 static void urClearOps (void) {
642 while (oplist) {
643 UrAsmOp *c = oplist;
645 oplist = oplist->next;
646 free(c->name);
647 free(c);
652 ///////////////////////////////////////////////////////////////////////////////
653 // label management
655 typedef struct UrLabelInfo {
656 char *name;
657 int32_t value;
658 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
659 int known; /* !0: label value already known */
660 int refLine; /* first referenced line */
661 int fixuptype; /* UR_FIXUP_XXX */
662 char *refFile;
663 struct UrLabelInfo *next;
664 } UrLabelInfo;
666 static UrLabelInfo *labels = NULL;
667 static UrLabelInfo *labelsTail = NULL;
670 static void urClearLabels (void) {
671 UrLabelInfo *c;
672 while ((c = labels) != NULL) {
673 labels = c->next;
674 if (c->name) free(c->name);
675 if (c->refFile) free(c->refFile);
676 free(c);
678 labelsTail = NULL;
682 static UrLabelInfo *urFindLabel (const char *name) {
683 for (UrLabelInfo *c = labels; c; c = c->next) if (strcmp(name, c->name) == 0) return c;
684 return NULL;
688 static UrLabelInfo *urAddLabel (const char *name) {
689 UrLabelInfo *c = urFindLabel(name);
690 if (c == NULL) {
691 c = calloc(1, sizeof(UrLabelInfo));
692 if (!c) abort();
693 c->name = strdup(name);
694 c->type = -1;
695 c->fixuptype = UR_FIXUP_NONE;
696 c->next = NULL;
697 if (labelsTail) labelsTail->next = c; else labels = c;
698 labelsTail = c;
700 return c;
704 ///////////////////////////////////////////////////////////////////////////////
705 // module list management
707 typedef struct ModuleInfo {
708 char *name;
709 char *fname; // opened in this file
710 int seen; // !0: module already seen, skip other definitions from the same file
711 struct ModuleInfo *next;
712 } ModuleInfo;
714 static ModuleInfo *modules = NULL;
715 static ModuleInfo *curModule = NULL;
718 static void modulesClear (void) {
719 curModule = NULL;
720 while (modules) {
721 ModuleInfo *c = modules;
723 modules = modules->next;
724 free(c->name);
725 free(c->fname);
726 free(c);
731 static void modulesResetSeen (void) {
732 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
736 static ModuleInfo *moduleFind (const char *name) {
737 if (!name || !name[0]) return NULL;
738 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
739 return NULL;
743 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
744 ModuleInfo *c;
746 if (!name || !fname || !name[0] || !fname[0]) abort();
747 if ((c = calloc(1, sizeof(ModuleInfo))) == NULL) abort();
748 if ((c->name = strdup(name)) == NULL) abort();
749 if ((c->fname = strdup(fname)) == NULL) abort();
750 c->next = modules;
751 return (modules = c);
755 ///////////////////////////////////////////////////////////////////////////////
756 // fixup management
757 typedef struct FixupItem {
758 struct FixupItem *next;
759 uint16_t opdestaddr;
760 uint16_t opaddr;
761 int fixuptype;
762 int size;
763 } FixupItem;
764 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
767 static void clearFixups (void) {
768 FixupItem *c;
770 while ((c = fixlisthead) != NULL) {
771 fixlisthead = c->next;
772 free(c);
777 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
778 FixupItem *fx = calloc(1, sizeof(FixupItem));
780 fx->opdestaddr = opdestaddr;
781 fx->opaddr = opaddr;
782 fx->fixuptype = fixuptype;
783 fx->size = size;
785 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
786 fx->next = NULL;
787 fixlisttail = fx;
791 ///////////////////////////////////////////////////////////////////////////////
792 // destination memory management
794 static uint8_t memory[65536];
795 static char memused[65536];
796 static char memresv[65536];
797 static uint16_t start_pc = 0x100; // viva CP/M!
798 static uint16_t start_disp = 0x100; // viva CP/M!
799 static uint16_t start_ent = 0x100; // viva CP/M!
800 static uint16_t pc = 0; /* current position to write */
801 static uint16_t disp = 0; /* current 'virtual PC' */
802 static uint16_t ent = 0; /* starting address */
803 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
804 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
805 static int inTapeBlock = 0;
806 static uint8_t tapeXorB = 0;
809 static inline uint8_t getByte (uint16_t addr) {
810 return memory[addr];
815 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
816 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
821 static inline void putByte (uint16_t addr, uint8_t b) {
822 if (inTapeBlock) tapeXorB ^= b;
823 memory[addr] = b;
824 memused[addr] = 1;
828 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
829 putByte(addr, w&0xFFU);
830 putByte(addr+1, (w>>8)&0xFFU);
834 static inline void emitByte (uint8_t b) {
835 putByte(pc, b);
836 ++pc;
837 ++disp;
841 static inline void emitWord (uint16_t w) {
842 emitByte(w&0xFFU);
843 emitByte((w>>8)&0xFFU);
847 static inline void emitRWord (uint16_t w) {
848 emitByte((w>>8)&0xFFU);
849 emitByte(w&0xFFU);
853 static void prepareMemory (void) {
854 memset(memory, 0, sizeof(memory));
855 memset(memused, 0, sizeof(memused));
856 memset(memresv, 0, sizeof(memresv));
860 ///////////////////////////////////////////////////////////////////////////////
861 // label getter and utilities
863 static char *lastSeenGlobalLabel = NULL; /* global */
866 static char *fixLocalLabel (const char *name) {
867 static char newname[MAX_LINE_SIZE*2+1024];
869 memset(newname, 0, sizeof(newname));
870 if (!name || !name[0]) {
871 newname[0] = '\0';
872 } else if (!lastSeenGlobalLabel || name[0] != '.') {
873 strcpy(newname, name);
874 } else {
875 if (name[0] == '.' && name[1] == '.') {
876 // this is macro label
877 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
878 sprintf(newname, "{#MAC%d:%s:%s}", curmacronum, curmacro->mac->name, name);
879 } else {
880 // this is local label, let's rename it
881 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
883 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
885 return newname;
889 static char *fixGlobalLabel (const char *name) {
890 static char newname[MAX_LINE_SIZE*2+1024];
892 memset(newname, 0, sizeof(newname));
893 if (!name || !name[0]) {
894 newname[0] = '\0';
895 } else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
896 if (name[0] == '@' && name[1]) ++name;
897 strcpy(newname, name);
898 } else {
899 // this is global unqualified label and we have a module; let's rename it
900 sprintf(newname, "%s.%s", curModule->name, name);
902 //printf("%s --> %s\n", name, newname);
903 return newname;
907 static int lblOptMakeU2 = 0;
909 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
910 UrLabelInfo *lbl;
911 char *ln, *nn;
913 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
914 lbl = urFindLabel(nn);
915 if (!lbl) {
916 // try non-module label
917 lbl = urFindLabel(ln);
919 if (!lbl) {
920 if (pass != 0) {
921 errorMsg("using undefined label %s", ln);
922 *found = 0;
923 *defined = 0;
924 return 0;
926 lbl = urAddLabel(nn);
927 lbl->type = (lblOptMakeU2 ? -42 : -1);
928 lbl->known = 0;
929 lbl->refLine = curSrcLine->lineNo;
930 lbl->refFile = strdup(curSrcLine->fname);
931 //fprintf(stderr, "new label: [%s]\n", lbl->name);
932 //fprintf(stderr, "new label: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
933 } else {
934 //fprintf(stderr, "label reference: [%s] (%d : #%04X); disp=#%04X; pc=#%04X\n", lbl->name, lbl->value, lbl->value&0xffffU, disp, pc);
936 if (lbl) {
937 *found = 1;
938 *defined = lbl->known!=0;
939 *fixuptype = lbl->fixuptype;
940 return lbl->value;
942 *found = 0;
943 *defined = 0;
944 return 0;
948 // qtypes
949 enum {
950 UR_QTYPE_DEFINED,
951 UR_QTYPE_KNOWN
954 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
955 UrLabelInfo *lbl;
956 char *ln, *nn;
958 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
959 lbl = urFindLabel(nn);
960 if (!lbl) {
961 // try non-module label
962 lbl = urFindLabel(ln);
964 switch (qtype) {
965 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
966 case UR_QTYPE_KNOWN: return lbl!=NULL;
967 default: ;
969 return 0;
973 static void fixupOperandCB (const urasm_operand_t *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
974 if (pass == 1) {
975 //static const char *n[4] = {"none", "word", "low", "high"};
976 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
977 addFixup(opdestaddr, opaddr, fixuptype, size);
982 static int checkLabels (void) {
983 int wasError = 0;
984 for (UrLabelInfo *c = labels; c; c = c->next) {
985 //fprintf(stderr, ":LBL: <%s> (type=%d)\n", c->name, c->type);
986 if (c->type == -1) {
987 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
988 wasError = 1;
990 if (c->type == 0) c->known = -1;
992 //if (wasError) longjmp(errJP, 667);
993 return wasError;
997 ///////////////////////////////////////////////////////////////////////////////
998 // expression utils (aka string parsing)
1001 /* skip leading spaces */
1002 /* returns string with spaces skipped */
1003 static inline char *strSkipSpaces (const char *s) {
1004 while (*s && isSpace(*s)) ++s;
1005 return (char *)s;
1009 /* skip leading spaces */
1010 /* returns string with spaces skipped */
1011 static inline const char *strSkipSpacesConst (const char *s) {
1012 while (*s && isSpace(*s)) ++s;
1013 return s;
1017 /* remove trailing spaces from string */
1018 static void strTrimRight (char *s) {
1019 if (!s || !s[0]) return;
1020 size_t len = strlen(s);
1021 while (len > 0 && isSpace(s[len-1])) --len;
1022 s[len] = 0;
1026 /* skip leading spaces and colons */
1027 /* returns string with spaces skipped */
1028 static inline char *strSkipSpacesColons (char *s) {
1029 while (*s && (isSpace(*s) || *s == ':')) ++s;
1030 return s;
1034 /* remove leading spaces from the current line */
1035 static inline void removeSpaces (void) {
1036 char *e = strSkipSpaces(currLine);
1037 if (e != currLine) memmove(currLine, e, strlen(e)+1);
1041 /* skip leading spaces, and argument (up to, but not including comma or colon) */
1042 /* correctly skip strings */
1043 /* returns string after skipped argument (with trailing spaces skipped) */
1044 static char *skipMacroArg (char *str) {
1045 int parens = 0;
1046 char *strstart = str;
1047 for (;;) {
1048 str = strSkipSpaces(str);
1049 if (!str[0]) return str;
1050 if (parens == 0 && (str[0] == ',' || str[0] == ':')) return str;
1051 /* check for "af'" */
1052 if (str[0] == '\'' && str-strstart >= 2 && toLower(str[-2] == 'a') && toLower(str[-1] == 'f')) {
1053 ++str;
1054 continue;
1056 if (str[0] == '(') { ++parens; continue; }
1057 if (str[0] == ')') { if (--parens < 0) parens = 0; continue; }
1058 /* check for string */
1059 if (str[0] == '"' || str[0] == '\'') {
1060 const char qch = *str++;
1061 while (*str) {
1062 const char ch = *str++;
1063 if (ch == qch) break;
1064 if (ch == '\\' && *str) ++str;
1066 continue;
1068 ++str;
1073 /* evaluate next numeric expression in input string */
1074 /* returns expression value */
1075 static int32_t getExprArg (int *defined, int *addr) {
1076 int error = 0;
1077 char *a = strSkipSpaces(currLine);
1078 int32_t res;
1079 if (!a[0]) fatal("expression expected");
1080 const char *ee = urasm_expr(&res, a, disp, defined, addr, &error);
1081 if (error) fatalUrLib(error);
1082 if (*ee) {
1083 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
1084 memmove(currLine, ee, strlen(ee)+1);
1085 } else {
1086 currLine[0] = '\0';
1088 return res;
1092 /* evaluate next string expression in input string */
1093 /* returns expression value */
1094 static char *getStrExprArg (void) {
1095 int error = 0;
1096 int donteval = 0, defined = 0;
1097 static char resbuf[256];
1098 char *a = strSkipSpaces(currLine);
1099 if (!a[0]) fatal("expression expected");
1100 urasm_exprval_t res;
1101 urasm_exprval_init(&res);
1102 const char *ee = urasm_expr_ex(&res, a, disp, &donteval, &defined, &error);
1103 if (error) fatalUrLib(error);
1104 if (*ee) {
1105 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
1106 memmove(currLine, ee, strlen(ee)+1);
1107 } else {
1108 currLine[0] = '\0';
1110 if (res.str) {
1111 snprintf(resbuf, sizeof(resbuf), "%s", res.str);
1112 } else {
1113 snprintf(resbuf, sizeof(resbuf), "%d", res.val);
1115 urasm_exprval_clear(&res);
1116 return resbuf;
1120 /* evaluate next numeric expression in input string */
1121 /* there shoild be no other expressions in the string */
1122 /* returns expression value */
1123 static int32_t getOneExprArg (int *defined, int *addr) {
1124 int32_t res = getExprArg(defined, addr);
1125 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
1126 return res;
1130 /* is next expression a string literal? */
1131 static inline int isStrArg (void) {
1132 const char *s = strSkipSpaces(currLine);
1133 return (s[0] == '"' || s[0] == '\'');
1137 /* check of we reached end of operator */
1138 static __attribute__((unused)) inline int isOperatorEnd (void) {
1139 const char *s = strSkipSpaces(currLine);
1140 return (s[0] == 0 || s[0] == ':');
1144 /* check of we reached end of operator */
1145 static __attribute__((unused)) inline int isLineEnd (void) {
1146 const char *s = strSkipSpaces(currLine);
1147 return (s[0] == 0);
1151 /* parse string argument from input string */
1152 /* returns parsed string */
1153 static char *getStrArg (int *lenp) {
1154 char *res, qCh;
1155 char *a = strSkipSpaces(currLine);
1156 qCh = *a++;
1157 if (qCh != '"' && qCh != '\'') fatal("string expected");
1158 res = parseStr(&a, qCh, lenp);
1159 if (*a) {
1160 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
1161 memmove(currLine, a, strlen(a)+1);
1162 } else {
1163 currLine[0] = '\0';
1165 return res;
1169 /* get identifier (and lowercase it) */
1170 static char *getOneIdArgLo (void) {
1171 static char res[MAX_LINE_SIZE+128];
1172 char *p;
1173 char *a = strSkipSpaces(currLine);
1174 memset(res, 0, sizeof(res));
1175 if (!a[0]) fatal("identifier expected");
1176 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1177 for (; p > res && isSpace(p[-1]); --p) {}
1178 *p = '\0';
1179 if (p-res > 120) fatal("identifier too long: %s", res);
1180 while (*a && isSpace(*a)) ++a;
1181 if (*a) {
1182 memmove(currLine, a, strlen(a)+1);
1183 if (currLine[0] == ';') currLine[0] = 0;
1184 if (currLine[0]) fatal("extra arguments");
1185 } else {
1186 currLine[0] = '\0';
1188 for (char *t = res; *t; ++t) *t = toLower(*t);
1189 return res;
1193 /* get label argument */
1194 static char *getLabelArg (int checkdelim) {
1195 static char res[MAX_LINE_SIZE+128];
1196 char *p;
1197 char *a = strSkipSpaces(currLine);
1198 memset(res, 0, sizeof(res));
1199 if (!a[0]) fatal("label expected");
1200 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1201 for (; p > res && isSpace(p[-1]); --p) {}
1202 *p = '\0';
1203 if (p-res > 120) fatal("label name too long: %s", res);
1204 while (*a && isSpace(*a)) ++a;
1205 if (*a) {
1206 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
1207 memmove(currLine, a, strlen(a)+1);
1208 } else {
1209 currLine[0] = '\0';
1211 return res;
1215 /* get label argument, and ensure that it is the last one */
1216 static char *getOneLabelArg (void) {
1217 char *res = getLabelArg(1);
1218 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
1219 return res;
1223 /* returns ',' or 0 */
1224 static char eatComma (void) {
1225 char *a = strSkipSpaces(currLine);
1226 if (!a[0]) { currLine[0] = '\0'; return 0; }
1227 if (a[0] == ':') return 0;
1228 if (a[0] != ',') fatal("invalid expression: ',' expected");
1229 for (++a; *a && isSpace(*a); ++a) {}
1230 if (!a[0]) { currLine[0] = '\0'; return 0; }
1231 memmove(currLine, a, strlen(a)+1);
1232 return ',';
1236 ///////////////////////////////////////////////////////////////////////////////
1237 // label processor
1239 static MAYBE_UNUSED void removeSpacesAndColons (void) {
1240 char *ep = strSkipSpacesColons(currLine);
1241 memmove(currLine, ep, strlen(ep)+1);
1245 static void checkExprEnd (void) {
1246 char *ep = strSkipSpaces(currLine);
1247 memmove(currLine, ep, strlen(ep)+1);
1248 if (currLine[0] && currLine[0] != ':') fatal("end of expression expected");
1252 static void checkOperatorEnd (void) {
1253 char *ep = strSkipSpaces(currLine);
1254 memmove(currLine, ep, strlen(ep)+1);
1255 if (currLine[0]) fatal("end of operator expected");
1259 /* remove label from curLine */
1260 static void removeLabel (void) {
1261 char *ep = currLine;
1262 if (ep[0] && !isSpace(ep[0])) for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {} // skip text
1263 // skip spaces and colons
1264 ep = strSkipSpacesColons(ep);
1265 memmove(currLine, ep, strlen(ep)+1);
1269 static int labelDoEQU (const char *lblname, const char *value) {
1270 static char n2[256];
1271 UrLabelInfo *lbl;
1273 if (value == NULL || lblname == NULL || !lblname[0] || strlen(lblname) > 255 || strlen(value) >= MAX_LINE_SIZE) return -1;
1274 memset(n2, 0, sizeof(n2));
1275 strcpy(n2, lblname);
1276 if (!urasm_is_valid_name(n2)) return -1; // this is not a valid label, get out of here
1277 // check if this can be an instruction
1278 lbl = urAddLabel(lblname);
1279 if (!lbl->refFile) {
1280 lbl->refLine = 0;
1281 lbl->refFile = strdup("artificially-defined-label");
1283 strcpy(currLine, value);
1285 int defined = 1, addr = UR_FIXUP_NONE;
1286 int32_t res = getOneExprArg(&defined, &addr);
1287 lbl->type = 1; // equ label
1288 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1289 if (defined) {
1290 lbl->value = res;
1291 lbl->known = 1;
1292 } else {
1293 return -1; //fatal("can't calculate label %s", lbl->name);
1296 return 0;
1300 static void processLabel (void) {
1301 char *argstart;
1302 char *ep, *ln, *nn;
1303 static char n2[256];
1304 UrLabelInfo *lbl;
1305 int noLocAff = 0, doEQU = 0;
1306 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1307 memset(n2, 0, sizeof(n2));
1308 if (!currLine[0] || isSpace(currLine[0]) || currLine[0] == ':') {
1309 // this may be " id = smth" or " id equ smth"
1310 ep = currLine;
1311 // skip spaces
1312 while (isSpace(*ep)) ++ep;
1313 if (ep[0] == ':' || !ep[0] || (!isAlpha(ep[0]) && ep[0] != '_' && ep[0] != '.' && ep[0] != '@')) {
1314 removeLabel(); // removeLabel() removes any spaces, etc
1315 return;
1317 // this looks like a label; check for '=' or 'equ'
1318 while (isAlphaDigit(ep[0]) || ep[0] == '_' || ep[0] == '.' || ep[0] == '@') ++ep;
1319 nn = ep;
1320 // skip trailing spaces
1321 while (isSpace(*ep)) ++ep;
1322 if (ep[0] == '=') {
1323 doEQU = 0;
1324 argstart = ++ep;
1325 } else if (isSpace(*nn)) {
1326 doEQU = 1;
1327 argstart = strIsCommand("EQU", ep);
1328 } else {
1329 argstart = NULL;
1331 if (!argstart) {
1332 removeLabel(); // removeLabel() removes any spaces, etc
1333 return;
1335 // remove leading spaces from name
1336 // copy label
1337 ep = currLine;
1338 // skip spaces
1339 while (isSpace(*ep)) ++ep;
1340 if (ep >= nn) fatal("internal compiler error");
1341 if (nn-ep > 120) fatal("label too long");
1342 memset(n2, 0, sizeof(n2));
1343 memmove(n2, ep, nn-ep);
1344 if (urFindOp(n2) || !urasm_is_valid_name(n2)) {
1345 //fatal("invalid label name");
1346 removeLabel(); // removeLabel() removes any spaces, etc
1347 return;
1349 // remove label name
1350 memmove(currLine, argstart, strlen(argstart)+1);
1351 // append label
1352 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1353 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1354 lbl = urAddLabel(nn);
1355 if (!lbl->refFile) {
1356 lbl->refLine = curSrcLine->lineNo;
1357 lbl->refFile = strdup(curSrcLine->fname);
1359 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
1360 if (doEQU && pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1361 int defined = 1, addr = UR_FIXUP_NONE;
1362 int32_t res = getOneExprArg(&defined, &addr);
1363 lbl->type = doEQU;
1364 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1365 if (defined) {
1366 lbl->value = res;
1367 lbl->known = 1;
1368 } else {
1369 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1371 currLine[0] = '\0';
1372 return;
1374 // collect label
1375 for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {}
1376 if (ep-currLine > 120) fatal("label too long");
1377 // copy label
1378 memset(n2, 0, sizeof(n2));
1379 memmove(n2, currLine, ep-currLine);
1380 if (urFindOp(n2)) {
1381 ep = strSkipSpaces(ep);
1382 if (*ep != ':') return; // this must be an instruction, process it
1384 if (!urasm_is_valid_name(n2)) return; // this is not a valid label, get out of here
1385 // check for macro
1386 if (findMacro(n2)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
1387 // check if this can be instruction
1388 //ep = strSkipSpaces(ep);
1389 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1390 // ok, we got a good label
1391 removeLabel();
1392 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1393 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1394 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1395 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1396 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1397 lbl = urAddLabel(nn);
1398 if (!lbl->refFile) {
1399 lbl->refLine = curSrcLine->lineNo;
1400 lbl->refFile = strdup(curSrcLine->fname);
1402 //printf("new: [%s]\n", lbl->name);
1403 // get command name
1404 if (currLine[0] == '=') {
1405 doEQU = 0;
1406 argstart = currLine+1;
1407 } else {
1408 doEQU = 1;
1409 argstart = strIsCommand("EQU", currLine);
1411 if (!argstart || doEQU) {
1412 if (pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1414 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d)\n", lbl->name, doEQU);
1415 if (argstart) {
1416 // do '=' or 'EQU'
1417 memmove(currLine, argstart, strlen(argstart)+1);
1418 if (!doEQU) {
1419 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label '%s'", lbl->name);
1421 int defined = 1, addr = UR_FIXUP_NONE;
1422 int32_t res = getOneExprArg(&defined, &addr);
1423 lbl->type = doEQU;
1424 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1425 if (defined) {
1426 lbl->value = res;
1427 lbl->known = 1;
1428 } else {
1429 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1431 currLine[0] = '\0';
1432 return;
1434 //fprintf(stderr, "**: LBL: <%s> (doEQU=%d); disp=#%04X; pc=#%04X\n", lbl->name, doEQU, disp, pc);
1435 // code label
1436 if (lbl->name[0] != '{' && !noLocAff) {
1437 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
1438 lastSeenGlobalLabel = strdup(lbl->name);
1440 lbl->type = 2;
1441 lbl->value = disp;
1442 lbl->known = 1;
1443 lbl->fixuptype = UR_FIXUP_WORD;
1447 ///////////////////////////////////////////////////////////////////////////////
1448 // instruction finder (in source)
1450 /* array ends with NULL */
1451 /* returns line or NULL */
1452 /* iidx will be set to found instruction number */
1453 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
1454 if (iidx) *iidx = -1;
1455 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
1456 va_list ap;
1457 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1458 va_start(ap, iidx);
1459 for (int f = 0; ;++f) {
1460 const char *name = va_arg(ap, const char *);
1462 if (!name) break;
1463 if (strIsCommand(name, cur->line)) {
1464 va_end(ap);
1465 if (iidx) *iidx = f;
1466 return cur;
1469 va_end(ap);
1471 return NULL;
1475 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
1476 return findNextInstructionFromList(NULL, name, NULL);
1480 ///////////////////////////////////////////////////////////////////////////////
1481 // writers
1483 static int optWriteFixups = 0;
1484 static int optFixupType = 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1485 static int optRunTape = 1;
1486 static int optRunDMB = 1;
1487 static int optRunSCL = -1; /* -1: save cargador, but no boot; 1: boot and cargador */
1488 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1489 static int optSNA48 = 1;
1490 static char *optOutputDir = NULL;
1493 ///////////////////////////////////////////////////////////////////////////////
1494 static void writeFixups (void) {
1496 void writeFixupList (FILE *fo, int cnt, const char *lbl, int (*chk)(const FixupItem *)) {
1497 if (cnt > 0) {
1498 int prevaddr = 0;
1499 fprintf(fo, "%s:\n", lbl);
1500 if (optFixupType == 1) fprintf(fo, " dw %d ; count\n", cnt);
1501 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1502 if (chk(fx)) {
1503 fprintf(fo, " dw #%04X\n", fx->opaddr-prevaddr);
1504 if (optFixupType == 2) {
1505 prevaddr = fx->opaddr;
1509 if (optFixupType == 2 || optFixupType == 3) fprintf(fo, " dw 0 ; end marker\n");
1513 if (optFixupType == 0) {
1514 /* simple text file */
1515 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.txt");
1516 printf("writing fixups to '%s'...\n", fname);
1517 FILE *fo = fopen(fname, "w");
1518 free(fname);
1519 if (fo == NULL) fatal("can't write fixup file");
1520 fprintf(fo, "; addr dadr sz\n");
1521 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1522 static const char type[4] = "NWLH";
1523 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
1525 fclose(fo);
1526 } else {
1527 /* various asm formats */
1528 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.zas");
1529 printf("writing fixups to '%s'...\n", fname);
1530 FILE *fo = fopen(fname, "w");
1531 int cntw = 0, cntwl = 0, cntwh = 0, cntbl = 0, cntbh = 0;
1532 free(fname);
1533 if (fo == NULL) fatal("can't write fixup file");
1534 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1535 if (fx->fixuptype == UR_FIXUP_WORD) { ++cntw; continue; }
1536 switch (fx->fixuptype) {
1537 case UR_FIXUP_LOBYTE: if (fx->size == 2) ++cntwl; else ++cntbl; break;
1538 case UR_FIXUP_HIBYTE: if (fx->size == 2) ++cntwh; else ++cntbh; break;
1541 writeFixupList(fo, cntw, "fixup_table_w", lambda(int, (const FixupItem *fx) {
1542 return (fx->fixuptype == UR_FIXUP_WORD);
1543 }));
1544 writeFixupList(fo, cntwl, "fixup_table_wl", lambda(int, (const FixupItem *fx) {
1545 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 2);
1546 }));
1547 writeFixupList(fo, cntwh, "fixup_table_wh", lambda(int, (const FixupItem *fx) {
1548 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 2);
1549 }));
1550 writeFixupList(fo, cntbl, "fixup_table_bl", lambda(int, (const FixupItem *fx) {
1551 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 1);
1552 }));
1553 writeFixupList(fo, cntbh, "fixup_table_bh", lambda(int, (const FixupItem *fx) {
1554 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 1);
1555 }));
1556 fclose(fo);
1561 ///////////////////////////////////////////////////////////////////////////////
1562 /* return 'found' flag */
1563 static int findChunkFrom (int addr, int *start, int *len) {
1564 if (addr < 0) addr = 0;
1565 for (; addr <= 65535 && !memused[addr]; ++addr) {}
1566 if (addr > 65535) return 0;
1567 *start = addr;
1568 for (; addr <= 65535 && memused[addr]; ++addr) {}
1569 *len = addr-(*start);
1570 return 1;
1574 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
1575 for (; buflen >= 2; buflen -= 2) {
1576 int cnt = *buf++;
1577 uint8_t byte = *buf++;
1579 if (cnt == 0) {
1580 // copy
1581 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
1582 if (!memused[addr]) putByte(addr, *buf);
1583 if (addr == 0xffff) return;
1585 } else {
1586 // fill
1587 for (; cnt > 0; --cnt, ++addr) {
1588 if (!memused[addr]) putByte(addr, byte);
1589 if (addr == 0xffff) return;
1596 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1597 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1598 if (bxor) *bxor = (*bxor)^b;
1599 return 0;
1603 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1604 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1605 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1606 return 0;
1610 ///////////////////////////////////////////////////////////////////////////////
1611 // .sna
1613 static int saveSna (const char *fname, int as48) {
1614 char *fn;// = malloc(strlen(fname)+16);
1615 uint8_t regs[27];
1616 FILE *fo;
1617 char abuf[32];
1619 fn = strprintf("%s/%s.sna", optOutputDir, fname);
1620 fo = fopen(fn, "wb");
1621 free(fn);
1622 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1623 printf("out: %s.sna\n", fname);
1624 memmove(regs, ursna48, 27);
1625 #if 0
1626 if (optRunSNA) {
1627 /* push new address */
1628 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1629 sp -= 2;
1630 putByte(sp, ent&0xFFU);
1631 putByte(sp+1, (ent>>8)&0xFFU);
1632 regs[23] = sp&0xFFU;
1633 regs[24] = (sp>>8)&0xFFU;
1635 fwrite(regs, 27, 1, fo);
1636 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1637 fwrite(memory+16384, 49152, 1, fo);
1638 #endif
1640 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1642 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1643 //fprintf(stderr, "%d\n", memused[sp]);
1644 if (memused[sp] || memused[(sp+1)&0xffff]) {
1645 fprintf(stderr, "FATAL: can't save snapshot!\n");
1646 goto error;
1649 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
1650 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
1651 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1653 sprintf(abuf, "%05d", clrAddr);
1654 memcpy(memory+23762, abuf, 5);
1655 sprintf(abuf, "%05d", ent);
1656 memcpy(memory+23773, abuf, 5);
1658 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
1660 if (!as48) {
1661 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
1663 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
1664 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
1665 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
1666 // write pages
1667 memset(memory, 0, 16384);
1668 for (int f = 1; f < 8; ++f) {
1669 if (f != 2 && f != 5) {
1670 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
1675 fclose(fo);
1676 return 0;
1677 error:
1678 fclose(fo);
1679 unlink(fname);
1680 return -1;
1684 ///////////////////////////////////////////////////////////////////////////////
1685 // bin chunks
1687 static void saveRaw (const char *basename) {
1688 char *fname = NULL;// = malloc(strlen(basename)+16);
1689 int start = 0, len;
1690 FILE *fo;
1692 while (findChunkFrom(start, &start, &len)) {
1693 if (fname != NULL) free(fname);
1694 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, (optTapExt ? "tap" : "bin"));
1695 fo = fopen(fname, "wb");
1696 if (!fo) {
1697 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1698 } else {
1699 printf("out: %s\n", fname);
1700 if (fwrite(memory+start, len, 1, fo) != 1) {
1701 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1702 fclose(fo);
1703 unlink(fname);
1704 } else {
1705 fclose(fo);
1708 start += len;
1710 if (fname != NULL) free(fname);
1714 ///////////////////////////////////////////////////////////////////////////////
1715 // HoBeta files
1717 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
1718 char name[8];
1719 char type;
1720 uint16_t start;
1721 uint16_t len;
1722 uint8_t zero; // always zero
1723 uint8_t secLen; // length in sectors
1724 uint16_t checksum;
1725 } HOBHeader;
1728 static uint16_t calcHobSum (const void *hdr) {
1729 const uint8_t *buf = (const uint8_t *)hdr;
1730 uint16_t res = 0;
1732 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
1733 return res;
1737 static void saveHob (const char *basename) {
1738 char *fname = NULL;//malloc(strlen(basename)+16);
1739 int start = 0, len;
1740 HOBHeader hdr;
1741 FILE *fo;
1743 while (findChunkFrom(start, &start, &len)) {
1744 if (fname != NULL) free(fname);
1745 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, "$C");
1746 fo = fopen(fname, "wb");
1747 if (!fo) {
1748 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1749 start += len;
1750 } else {
1751 char tmpbuf[sizeof(hdr.name)*2];
1752 printf("out: %s\n", fname);
1753 memset(&hdr, 0, sizeof(hdr));
1754 memset(&hdr.name, 32, 8);
1755 snprintf(tmpbuf, sizeof(tmpbuf), "%c%04X", basename[0], start);
1756 while (strlen(tmpbuf) < sizeof(hdr.name)) strcat(tmpbuf, " "); /* sorry! */
1757 memcpy(hdr.name, tmpbuf, sizeof(hdr.name));
1758 hdr.type = 'C';
1759 hdr.start = start;
1760 hdr.len = len;
1761 hdr.secLen = (len+255)/256;
1762 hdr.checksum = calcHobSum(&hdr);
1763 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
1764 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1765 fclose(fo);
1766 unlink(fname);
1767 goto quit;
1769 if (fwrite(memory+start, len, 1, fo) != 1) {
1770 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1771 fclose(fo);
1772 unlink(fname);
1773 goto quit;
1775 start += len;
1776 while (len%256) {
1777 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
1778 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1779 fclose(fo);
1780 unlink(fname);
1781 goto quit;
1783 ++len;
1784 //fprintf(stderr, ":%d\n", len%256);
1786 fclose(fo);
1789 quit:
1790 if (fname != NULL) free(fname);
1794 // ////////////////////////////////////////////////////////////////////////// //
1795 typedef struct {
1796 uint8_t fcount;
1797 uint8_t dir[14*256];
1798 uint32_t dirpos;
1799 uint32_t currfstartpos;
1800 uint8_t *data;
1801 size_t datapos;
1802 size_t datasize;
1803 } SCLFile;
1806 static void sclInit (SCLFile *scl) {
1807 memset(scl, 0, sizeof(*scl));
1808 scl->datasize = 2*80*16*256; /* maximum disk size */
1809 scl->data = malloc(scl->datasize);
1810 memset(scl->data, 0, scl->datasize);
1811 scl->currfstartpos = 0xffffffffu;
1815 static void sclClear (SCLFile *scl) {
1816 if (!scl) return;
1817 if (scl->data) free(scl->data);
1818 memset(scl, 0, sizeof(*scl));
1819 scl->currfstartpos = 0xffffffffu;
1823 static void sclStartFile (SCLFile *scl, const char *name, char type, uint16_t v0, uint16_t v1) {
1824 if (scl->fcount == 255) {
1825 fprintf(stderr, "FATAL: too many files in SCL!\n");
1826 exit(1);
1828 if (scl->currfstartpos != 0xffffffffu) {
1829 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1830 exit(1);
1832 int nlen = 0;
1833 while (nlen < 8 && name && name[nlen]) scl->dir[scl->dirpos++] = name[nlen++];
1834 while (nlen < 8) { scl->dir[scl->dirpos++] = ' '; ++nlen; }
1835 scl->dir[scl->dirpos++] = type;
1836 scl->dir[scl->dirpos++] = v0&0xff;
1837 scl->dir[scl->dirpos++] = (v0>>8)&0xff;
1838 scl->dir[scl->dirpos++] = v1&0xff;
1839 scl->dir[scl->dirpos++] = (v1>>8)&0xff;
1840 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1841 scl->currfstartpos = scl->datapos;
1845 static void sclEndFile (SCLFile *scl) {
1846 if (scl->currfstartpos == 0xffffffffu) {
1847 fprintf(stderr, "FATAL: last SCL file already finished!\n");
1848 exit(1);
1850 /* align to sector */
1851 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
1852 const uint32_t fsz = scl->datapos-scl->currfstartpos;
1853 if (fsz > 255*256) {
1854 fprintf(stderr, "FATAL: SCL file too big!\n");
1855 exit(1);
1857 if (fsz&255) abort();
1858 scl->dir[scl->dirpos++] = fsz/256; /* size in sectors */
1859 if (scl->dirpos%14) abort();
1860 ++scl->fcount;
1861 scl->currfstartpos = 0xffffffffu;
1865 static void sclWriteData (SCLFile *scl, const void *buf, size_t len) {
1866 if (scl->currfstartpos == 0xffffffffu) {
1867 fprintf(stderr, "FATAL: no open SCL file!\n");
1868 exit(1);
1870 if (!len) return;
1871 if (len > 255*256) {
1872 fprintf(stderr, "FATAL: SCL file too big!\n");
1873 exit(1);
1875 memcpy(scl->data+scl->datapos, buf, len);
1876 scl->datapos += len;
1880 static void sclWriteByte (SCLFile *scl, uint8_t b) {
1881 sclWriteData(scl, &b, 1);
1885 static void sclWriteWord (SCLFile *scl, uint16_t w) {
1886 uint8_t b = w&0xff;
1887 sclWriteData(scl, &b, 1);
1888 b = (w>>8)&0xff;
1889 sclWriteData(scl, &b, 1);
1893 static int sclFileWrite (FILE *fo, const void *buf, size_t count, uint32_t *checksum) {
1894 if (!count) return 0;
1895 const uint8_t *p = (const uint8_t *)buf;
1896 if (checksum) for (size_t n = count; n--; ++p) *checksum += (uint32_t)(*p);
1897 if (fwrite(buf, count, 1, fo) != 1) return -1;
1898 return 0;
1902 // <0: error; 0: ok
1903 static int sclSaveToFile (FILE *fo, SCLFile *scl) {
1904 if (scl->currfstartpos != 0xffffffffu) {
1905 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1906 exit(1);
1908 const char *sign = "SINCLAIR";
1909 uint32_t checksum = 0;
1910 // header
1911 if (sclFileWrite(fo, sign, 8, &checksum) != 0) return -1;
1912 if (sclFileWrite(fo, &scl->fcount, 1, &checksum) != 0) return -1;
1913 // directory
1914 if (sclFileWrite(fo, scl->dir, scl->dirpos, &checksum) != 0) return -1;
1915 // data
1916 if (sclFileWrite(fo, scl->data, scl->datapos, &checksum) != 0) return -1;
1917 // write checksum
1918 for (unsigned f = 0; f < 4; ++f) {
1919 const uint8_t b = (checksum>>(f*8))&0xff;
1920 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1922 // done
1923 return 0;
1927 // ////////////////////////////////////////////////////////////////////////// //
1928 static void saveSCLCargador (SCLFile *scl) {
1929 static uint8_t cargador[16384]; // should be enough for everyone
1930 int linestart = -1;
1931 int linenum = 0;
1932 int start = 0, len, pos;
1934 void putStr (const char *s) {
1935 for (; *s; ++s) cargador[pos++] = *s;
1938 void putNum (int num) {
1939 char buf[64];
1940 sprintf(buf, "%d", num);
1941 putStr(buf);
1944 void startLine () {
1945 ++linenum;
1946 linestart = pos;
1947 // number
1948 cargador[pos++] = (linenum>>8)&0xff;
1949 cargador[pos++] = linenum&0xff;
1950 // size (will be fixed later)
1951 cargador[pos++] = 0;
1952 cargador[pos++] = 0;
1955 void flushLine () {
1956 if (linestart >= 0) {
1957 const int size = pos-linestart-4;
1958 cargador[linestart+2] = size&0xff;
1959 cargador[linestart+3] = (size>>8)&0xff;
1961 linestart = -1;
1964 char cname[16];
1966 pos = 0;
1967 startLine();
1968 // CLEAR VAL "xxx"
1969 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
1970 // load blocks
1971 int cont = 1;
1972 while (findChunkFrom(start, &start, &len)) {
1973 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1974 if (cont) { putStr(":"); cont = 0; }
1975 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1976 // generate chunk name
1977 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
1978 putStr(cname);
1979 //" CODE
1980 putStr("\"\xaf\r");
1981 flushLine();
1982 startLine();
1983 start += len;
1985 // and run
1986 if (cont) { putStr(":"); cont = 0; }
1987 // RANDOMIZE USR VAL "xxx"
1988 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"");
1989 putStr("\r");
1990 flushLine();
1992 //putWord(0xaa80);
1993 //putWord(1); // autostart line
1995 // write to SCL
1996 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
1997 sclWriteData(scl, cargador, (size_t)pos);
1998 sclWriteByte(scl, 0x80);
1999 sclWriteByte(scl, 0xaa);
2000 sclWriteWord(scl, 1);
2001 sclEndFile(scl);
2005 static void saveSCL (const char *basename) {
2006 SCLFile scl;
2007 int fcount = 0;
2008 int start, len;
2009 char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
2010 // count files
2011 start = 0;
2012 while (findChunkFrom(start, &start, &len)) {
2013 ++fcount;
2014 start += len;
2016 if (fcount && optRunSCL) fcount += (optRunSCL > 0 ? 2 : 1); // +loader and boot
2017 if (fcount > 255) {
2018 fprintf(stderr, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname, fcount);
2019 exit(1);
2021 // create output file
2022 FILE *fo = fopen(fname, "wb");
2023 if (!fo) {
2024 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
2025 exit(1);
2027 // initialise SCL writer
2028 sclInit(&scl);
2029 // write loader
2030 if (fcount && optRunSCL) {
2031 // create simple boot
2032 if (optRunSCL > 0) {
2033 const uint8_t dasboot[] = {
2034 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"
2036 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
2037 sclWriteWord(&scl, 1); // line number
2038 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
2039 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
2040 // TR-DOS signature
2041 sclWriteByte(&scl, 0x80);
2042 sclWriteByte(&scl, 0xaa);
2043 // start line
2044 sclWriteWord(&scl, 0);
2045 sclEndFile(&scl);
2047 saveSCLCargador(&scl);
2049 // write chunks
2050 char cname[16];
2051 start = 0;
2052 while (findChunkFrom(start, &start, &len)) {
2053 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
2054 sclStartFile(&scl, cname, 'C', (unsigned)start, (unsigned)len);
2055 sclWriteData(&scl, memory+(unsigned)start, (unsigned)len);
2056 sclEndFile(&scl);
2057 start += len;
2059 if (sclSaveToFile(fo, &scl) < 0) {
2060 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
2061 fclose(fo);
2062 unlink(fname);
2063 exit(1);
2065 sclClear(&scl);
2066 fclose(fo);
2067 printf("out: %s\n", fname);
2068 if (fname != NULL) free(fname);
2072 ///////////////////////////////////////////////////////////////////////////////
2073 // .dmb
2075 static int saveDMB (const char *fname) {
2076 char *fn;// = malloc(strlen(fname)+16);
2077 int start = 0, len;
2078 uint16_t pcnt = 0;
2079 FILE *fo;
2081 fn = strprintf("%s/%s.dmb", optOutputDir, fname);
2082 fo = fopen(fn, "wb");
2083 free(fn);
2084 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
2086 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
2088 printf("out: %s.dmb\n", fname);
2089 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
2090 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
2091 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
2092 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
2094 start = 0;
2095 while (findChunkFrom(start, &start, &len)) {
2096 if (fWriteWord(fo, len, NULL) != 0) goto error;
2097 if (fWriteWord(fo, start, NULL) != 0) goto error;
2098 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
2099 start += len;
2101 fclose(fo);
2102 return 0;
2103 error:
2104 fclose(fo);
2105 unlink(fname);
2106 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
2107 return -1;
2111 ///////////////////////////////////////////////////////////////////////////////
2112 // .tap
2114 static char tapeLoaderName[16];
2117 static void saveTapCargador (FILE *fo) {
2118 // count blocks
2119 static uint8_t cargador[16384]; // should be enough for everyone
2120 int start = 0, len, pos, f;
2121 uint8_t bxor;
2124 void putStr (const char *s) {
2125 for (; *s; ++s) cargador[pos++] = *s;
2128 void putNum (int num) {
2129 char buf[64];
2130 sprintf(buf, "%d", num);
2131 putStr(buf);
2135 pos = 4;
2136 // number
2137 cargador[0] = 0; cargador[1] = 10;
2138 // size (will be fixed later)
2139 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
2140 // load blocks
2141 while (findChunkFrom(start, &start, &len)) {
2142 // :LOAD "" CODE
2143 putStr(":\xef\"\"\xaf");
2144 start += len;
2146 // and run
2147 // :RANDOMIZE USR VAL "xxx"
2148 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
2149 // patch len
2150 cargador[2] = (pos-4)&0xff;
2151 cargador[3] = ((pos-4)>>8)&0xff;
2152 // write header
2153 fWriteWord(fo, 19, NULL); // length of header
2154 bxor = 0;
2155 fWriteByte(fo, 0, &bxor); // header block
2156 fWriteByte(fo, 0, &bxor); // 'basic' flag
2157 if (tapeLoaderName[0]) {
2158 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2159 } else {
2160 fWriteByte(fo, 'c', &bxor);
2161 fWriteByte(fo, 'a', &bxor);
2162 fWriteByte(fo, 'r', &bxor);
2163 fWriteByte(fo, 'g', &bxor);
2164 fWriteByte(fo, 'a', &bxor);
2165 fWriteByte(fo, 'd', &bxor);
2166 fWriteByte(fo, 'o', &bxor);
2167 fWriteByte(fo, 'r', &bxor);
2168 fWriteByte(fo, ' ', &bxor);
2169 fWriteByte(fo, ' ', &bxor);
2171 fWriteWord(fo, pos, &bxor); // length
2172 fWriteWord(fo, 10, &bxor); // start
2173 fWriteWord(fo, pos, &bxor); // length2
2174 fWriteByte(fo, bxor, NULL);
2175 // write data
2176 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
2177 bxor = 0;
2178 fWriteByte(fo, 0xFFU, &bxor); // data block
2179 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
2180 fWriteByte(fo, bxor, NULL);
2184 static void saveTap (const char *basename) {
2185 char *fname;// = malloc(strlen(basename)+16);
2186 char blkname[128];
2187 int start = 0, len, f;
2188 uint8_t bxor;
2189 FILE *fo;
2191 fname = strprintf("%s/%s.tap", optOutputDir, basename);
2192 fo = fopen(fname, "wb");
2193 free(fname);
2194 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
2195 printf("out: %s.tap\n", basename);
2196 if (optRunTape) saveTapCargador(fo);
2197 while (findChunkFrom(start, &start, &len)) {
2198 // write header
2199 if (tapeLoaderName[0]) {
2200 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2201 memcpy(blkname, tapeLoaderName, 10);
2202 } else {
2203 sprintf(blkname, "c%04X:%04X", start, len);
2205 //printf(" block: %s\n", blkname);
2206 fWriteWord(fo, 19, NULL); // length of header
2207 bxor = 0;
2208 fWriteByte(fo, 0, &bxor); // header block
2209 fWriteByte(fo, 3, &bxor); // 'code' flag
2210 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
2211 fWriteWord(fo, len, &bxor);
2212 fWriteWord(fo, start, &bxor);
2213 fWriteWord(fo, 32768, &bxor);
2214 fWriteByte(fo, bxor, NULL);
2215 // write data
2216 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
2217 bxor = 0;
2218 fWriteByte(fo, 0xFFU, &bxor); // data block
2219 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
2220 fWriteByte(fo, bxor, NULL);
2221 start += len;
2223 fclose(fo);
2227 ///////////////////////////////////////////////////////////////////////////////
2228 // pseudoinstructions
2230 // note that processCurrentLine() will NOT skip to the next line before
2231 // calling pseudoinstruction handler!
2232 // note that processCurrentLine() will skip current line after calling
2233 // pseudoinstruction handler!
2235 static int wasOrg = 0;
2236 static int wasClr = 0;
2239 // ////////////////////////////////////////////////////////////////////////// //
2240 // print message using printf-like syntax
2241 // doesn't print new line
2242 static void processPrintf (FILE *fo, const char *fmt) {
2243 if (!fmt || !fmt[0]) return;
2244 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2245 char *tempstr = NULL;
2246 size_t tempsize = 0;
2247 char *fmtcopy = malloc(strlen(fmt)+1);
2248 strcpy(fmtcopy, fmt);
2249 char *currfmt = fmtcopy;
2250 char tempbuf[512];
2251 int stlen;
2252 char *strarg;
2253 int defined;
2254 int32_t exprval;
2255 while (*currfmt) {
2256 /* find '%' */
2257 char *prcs = strchr(currfmt, '%');
2258 if (!prcs || !prcs[1]) {
2259 /* no more formatting; print the tail and exit the loop */
2260 fprintf(fo, "%s", currfmt);
2261 break;
2263 /* is this "%%"? */
2264 int docheck = 1;
2265 if (prcs[1] == '%') {
2266 ++prcs;
2267 docheck = 0;
2269 /* print up to `prcs` */
2270 if (prcs > currfmt) {
2271 size_t partlen = (ptrdiff_t)(prcs-currfmt);
2272 if (partlen+1 > tempsize) {
2273 tempsize = ((partlen+8)|0xff)+1;
2274 tempstr = realloc(tempstr, tempsize);
2276 memcpy(tempstr, currfmt, partlen);
2277 tempstr[partlen] = 0;
2278 fprintf(fo, "%s", tempstr);
2280 currfmt = ++prcs; /* skip percent */
2281 if (!docheck) continue;
2282 /* parse format */
2283 char sign = ' ';
2284 int width = 0;
2285 int zerofill = 0;
2286 char ftype = ' ';
2287 if (*currfmt == '+' || *currfmt == '-') sign = *currfmt++;
2288 if (sign != '-' && *currfmt == '0') zerofill = 1;
2289 while (isDigit(*currfmt)) { width = width*10+((*currfmt)-'0'); ++currfmt; }
2290 if (width > 256) width = 256;
2291 ftype = *currfmt++;
2292 if (!ftype) break; /* oops */
2293 if (!eatComma()) fatal("out of arguments for string format");
2294 switch (ftype) {
2295 case 's': /* string */
2296 stlen = 0;
2297 switch (strSkipSpaces(currLine)[0]) {
2298 case '"': case '\'': /* string literal? */
2299 strarg = getStrArg(&stlen);
2300 break;
2301 default: /* expression */
2302 strarg = getStrExprArg();
2303 stlen = (int)strlen(strarg);
2304 break;
2306 /* left pad */
2307 if (sign != '-' && stlen < width) {
2308 int padlen = width-stlen;
2309 memset(tempbuf, ' ', padlen);
2310 tempbuf[padlen+1] = 0;
2311 fprintf(fo, "%s", tempbuf);
2313 fprintf(fo, "%s", strarg);
2314 /* right pad */
2315 if (sign == '-' && stlen < width) {
2316 int padlen = width-stlen;
2317 memset(tempbuf, ' ', padlen);
2318 tempbuf[padlen+1] = 0;
2319 fprintf(fo, "%s", tempbuf);
2321 break;
2322 case 'd': /* decimal */
2323 defined = 1;
2324 exprval = getExprArg(&defined, NULL);
2325 if (width && zerofill) {
2326 fprintf(fo, "%0*d", width, exprval);
2327 } else if (width) {
2328 fprintf(fo, "%*d", (sign != '-' ? width : -width), exprval);
2329 } else {
2330 fprintf(fo, "%d", exprval);
2332 break;
2333 case 'c': /* char */
2334 defined = 1;
2335 exprval = getExprArg(&defined, NULL);
2336 if (exprval <= 0 || exprval == '?' || exprval > 255) exprval = '?';
2337 if (width) {
2338 fprintf(fo, "%*c", (sign != '-' ? width : -width), exprval);
2339 } else {
2340 fprintf(fo, "%c", exprval);
2342 break;
2343 case 'u': /* unsigned */
2344 defined = 1;
2345 exprval = getExprArg(&defined, NULL);
2346 if (width && zerofill) {
2347 fprintf(fo, "%0*u", width, (unsigned)(exprval&0xffff));
2348 } else if (width) {
2349 fprintf(fo, "%*u", (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2350 } else {
2351 fprintf(fo, "%u", (unsigned)(exprval&0xffff));
2353 break;
2354 case 'x': case 'X': /* hex */
2355 defined = 1;
2356 exprval = getExprArg(&defined, NULL);
2357 if (width && zerofill) {
2358 fprintf(fo, (ftype == 'x' ? "%0*x" : "%0*X"), width, (unsigned)(exprval&0xffff));
2359 } else if (width) {
2360 fprintf(fo, (ftype == 'x' ? "%*x" : "%*X"), (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2361 } else {
2362 fprintf(fo, (ftype == 'x' ? "%x" : "%X"), (unsigned)(exprval&0xffff));
2364 break;
2365 default:
2366 if (ftype <= 0 || ftype == 127) ftype = '?';
2367 fatal("invalid format specifier: '%c'", ftype);
2368 break;
2371 if (tempstr) free(tempstr);
2372 free(fmtcopy);
2376 ///////////////////////////////////////////////////////////////////////////////
2377 // user error
2379 static int piERROR (void) {
2380 int len = 0;
2381 char *res = getStrArg(&len);
2382 fprintf(stdout, "*** USER ERROR: ");
2383 processPrintf(stdout, res);
2384 fputc('\n', stdout);
2385 fatal("user error abort");
2386 return PI_SKIP_LINE;
2390 static int piWARNING (void) {
2391 int len = 0;
2392 char *res = getStrArg(&len);
2393 fprintf(stdout, "*** USER WARNING ");
2394 if (curSrcLine) {
2395 fprintf(stdout, "at file %s, line %d: ", curSrcLine->fname, curSrcLine->lineNo);
2396 } else {
2397 fprintf(stdout, "somewhere in time: ");
2399 processPrintf(stdout, res);
2400 fputc('\n', stdout);
2401 return PI_SKIP_LINE;
2405 ///////////////////////////////////////////////////////////////////////////////
2406 // user warnings
2408 static int piPrintfCommon (int passNo) {
2409 if (passNo < 0 || pass == passNo) {
2410 int len = 0;
2411 char *res = getStrArg(&len);
2412 processPrintf(stdout, res);
2413 fputc('\n', stdout);
2415 return PI_SKIP_LINE;
2419 static int piDISPLAYX (int passNo, int asHex) {
2420 for (;;) {
2421 if (isStrArg()) {
2422 int len = 0;
2423 char *res = getStrArg(&len);
2425 if (passNo < 0 || pass == passNo) printf("%s", res);
2426 } else {
2427 int defined = 1;
2428 int32_t v = getExprArg(&defined, NULL);
2430 if (passNo < 0 || pass == passNo) {
2431 if (asHex) printf("%04X", (unsigned int)v);
2432 else printf("%d", v);
2435 if (!eatComma()) break;
2437 return PI_SKIP_LINE;
2441 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2442 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2443 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2444 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2445 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2446 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2448 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2449 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2450 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2453 ///////////////////////////////////////////////////////////////////////////////
2454 // ORG, DISP, etc.
2456 static int piORG (void) {
2457 int defined = 1;
2458 int32_t res = getOneExprArg(&defined, NULL);
2460 if (!defined) fatal("sorry, ORG operand value must be known here");
2461 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
2462 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
2463 pc = disp = res;
2464 if (!wasOrg) {
2465 wasOrg = 1;
2466 ent = res;
2467 if (!wasClr && res > 0) clrAddr = res-1;
2469 return PI_CONT_LINE;
2473 static int piDISP (void) {
2474 int defined = 1;
2475 int32_t res = getOneExprArg(&defined, NULL);
2477 if (!defined) fatal("sorry, DISP operand value must be known here");
2478 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
2479 //printf("DISP=%d\n", res);
2480 disp = res;
2481 return PI_CONT_LINE;
2485 static int piENDDISP (void) {
2486 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2487 checkExprEnd();
2488 disp = pc;
2489 return PI_CONT_LINE;
2493 static int piENT (void) {
2494 int defined = 1;
2495 int32_t res = getOneExprArg(&defined, NULL);
2497 //if (!defined) fatal("sorry, ENT operand value must be known here");
2498 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
2499 ent = res;
2500 return PI_CONT_LINE;
2504 static int piCLR (void) {
2505 int defined = 1;
2506 int32_t res = getOneExprArg(&defined, NULL);
2508 //if (!defined) fatal("sorry, CLR operand value must be known here");
2509 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
2510 clrAddr = res;
2511 wasClr = 1;
2512 return PI_CONT_LINE;
2516 static int piRESERVE (void) {
2518 int defined = 1, start;
2519 int32_t res = getExprArg(&defined, NULL);
2520 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2521 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2522 start = res;
2523 if (!eatComma()) fatal("RESERVE needs 2 args");
2524 res = getOneExprArg(&defined, NULL);
2525 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2526 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2527 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2529 fatal("RESERVE: not yet!");
2530 return PI_CONT_LINE;
2534 static int piALIGN (void) {
2535 int defined = 1;
2536 int32_t res;
2538 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2539 res = getOneExprArg(&defined, NULL);
2540 if (!defined) fatal("sorry, ALIGN operand value must be known here");
2541 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
2542 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
2543 if (res > 0 && pc%res != 0) {
2544 pc /= res;
2545 pc *= res;
2546 pc += res;
2547 disp = pc;
2549 return PI_CONT_LINE;
2553 static int piDISPALIGN (void) {
2554 int defined = 1;
2555 int32_t res = getOneExprArg(&defined, NULL);
2557 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
2558 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
2559 if (res > 0 && disp%res != 0) {
2560 disp /= res;
2561 disp *= res;
2562 disp += res;
2564 return PI_CONT_LINE;
2568 ///////////////////////////////////////////////////////////////////////////////
2569 // DEFx
2571 static int defIncr = 0;
2574 static int piDEFINCR (void) {
2575 int defined = 1;
2576 int32_t cnt = getOneExprArg(&defined, NULL);
2578 if (!defined) fatal("DEFINCR: increment must be defined");
2579 defIncr = cnt;
2580 return PI_CONT_LINE;
2584 static int piDEFBW (int isWord) {
2585 for (;;) {
2586 int defined = 0, fixuptype = UR_FIXUP_NONE;
2588 if (isStrArg()) {
2589 int f, len = 0;
2590 char *res = getStrArg(&len);
2592 for (f = 0; f < len; ++f) {
2593 int32_t b = (uint8_t)res[f];
2594 b += defIncr;
2595 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
2596 if (b < 0) b += 256;
2597 emitByte(b);
2599 } else {
2600 int32_t res = getExprArg(&defined, &fixuptype);
2602 if (pass > 0 && !defined) fatal("undefined operand");
2603 res += defIncr;
2604 if (isWord) {
2605 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
2606 if (res < 0) res += 65536;
2607 if (isWord == 1) {
2608 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 2);
2609 emitWord(res);
2610 } else {
2611 /* reversed word */
2612 switch (fixuptype) {
2613 case UR_FIXUP_WORD: /* swapped bytes */
2614 urasm_fixup_operand(NULL, pc, disp, UR_FIXUP_HIBYTE, 1);
2615 urasm_fixup_operand(NULL, pc, disp+1, UR_FIXUP_LOBYTE, 1);
2616 break;
2617 case UR_FIXUP_LOBYTE:
2618 case UR_FIXUP_HIBYTE:
2619 warningMsg("non-word fixup for reversed word; wtf?!");
2620 break;
2621 default: break;
2623 emitRWord(res);
2625 } else {
2626 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
2627 if (fixuptype != UR_FIXUP_NONE) {
2628 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2629 urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2631 if (res < 0) res += 256;
2632 emitByte(res);
2635 if (!eatComma()) break;
2637 return PI_CONT_LINE;
2640 static int piDEFB (void) { return piDEFBW(0); }
2641 static int piDEFW (void) { return piDEFBW(1); }
2642 static int piDEFR (void) { return piDEFBW(2); }
2645 static int piDEFS (void) {
2646 for (;;) {
2647 int32_t bt, f;
2648 int defined = 0, fixuptype = UR_FIXUP_NONE;
2649 int32_t res = getExprArg(&defined, &fixuptype);
2651 if (pass > 0 && !defined) fatal("undefined operand");
2652 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
2653 if (*currLine && currLine[0] == ',') {
2654 (void)eatComma();
2655 bt = getExprArg(&defined, NULL);
2656 if (pass > 0 && !defined) fatal("undefined operand");
2657 bt += defIncr;
2658 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
2659 if (bt < 0) bt += 256;
2660 if (fixuptype != UR_FIXUP_NONE) {
2661 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2663 for (f = 0; f < res; ++f) {
2664 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2665 emitByte(bt);
2667 } else {
2668 pc += res; disp += res;
2670 if (!eatComma()) break;
2672 return PI_CONT_LINE;
2676 /* bit 0: put '\0' */
2677 /* bit 1: set bit 7 of last byte */
2678 /* bit 2: put length */
2679 static int piDEFSTR (int type) {
2680 for (;;) {
2681 if (isStrArg()) {
2682 int f, len = 0;
2683 char *res = getStrArg(&len);
2685 if (type&0x04) {
2686 if (len > 255) fatal("string too long");
2687 emitByte(len);
2689 for (f = 0; f < len; ++f) {
2690 uint8_t b = (uint8_t)res[f];
2692 if ((type&0x02) && f == len-1) b |= 0x80;
2693 emitByte(b);
2695 if (type&0x01) emitByte(0);
2696 } else {
2697 int defined = 1;
2698 int32_t v = getExprArg(&defined, NULL);
2700 if (pass > 0 && !defined) fatal("undefined expression");
2701 if (!defined) v = 0; else v += defIncr;
2702 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
2703 if (v < 0) v += 256;
2704 emitByte(v);
2706 if (!eatComma()) break;
2708 return PI_CONT_LINE;
2712 static int piDEFM (void) { return piDEFSTR(0x00); }
2713 static int piDEFZ (void) { return piDEFSTR(0x01); }
2714 static int piDEFX (void) { return piDEFSTR(0x02); }
2715 static int piDEFC (void) { return piDEFSTR(0x04); }
2718 ///////////////////////////////////////////////////////////////////////////////
2719 // INCBIN
2721 /* INCBIN "name"[,maxlen] */
2722 static int piINCBIN (void) {
2723 int system = 0;
2724 char *fn, qCh;
2725 uint8_t bt;
2726 FILE *fl;
2727 int maxlen = 65536;
2728 char *args = currLine;
2730 if (!currLine[0]) fatal("INCBIN without file name");
2731 if (isStrArg()) {
2732 qCh = *args++;
2733 system = 0;
2734 } else if (currLine[0] == '<') {
2735 qCh = '>'; ++args;
2736 system = 1;
2737 } else {
2738 qCh = 0;
2739 system = 0;
2741 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2742 if (!fn[0]) fatal("INCBIN: empty file name");
2743 memmove(currLine, args, strlen(args)+1);
2744 // maxlen
2745 if (currLine[0] == ',') {
2746 int defined = 1;
2747 (void)eatComma();
2748 maxlen = getOneExprArg(&defined, NULL);
2749 if (!defined) fatal("INCBIN: undefined maxlen");
2750 if (maxlen < 1) return 1; // nothing to do
2752 // now fix name
2753 char *fname = createIncludeName(fn, system, NULL);
2754 fl = fopen(fname, "rb");
2755 if (!fl) fatal("INCBIN: file not found: '%s'", fname);
2756 while (maxlen-- > 0) {
2757 int res = fread(&bt, 1, 1, fl);
2758 if (!res) break;
2759 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: '%s'", fname); }
2760 emitByte(bt);
2762 fclose(fl);
2763 free(fname);
2764 return PI_SKIP_LINE;
2768 ///////////////////////////////////////////////////////////////////////////////
2769 // INCLUDE
2771 /* INCLUDE "name" */
2772 static int piINCLUDE (void) {
2773 int system = 0;
2774 char *fn, qCh;
2775 char *args = currLine;
2777 if (!currLine[0]) fatal("INCLUDE without file name");
2778 if (isStrArg()) {
2779 qCh = *args++;
2780 system = 0;
2781 } else if (currLine[0] == '<') {
2782 qCh = '>'; ++args;
2783 system = 1;
2784 } else {
2785 qCh = 0;
2786 system = 0;
2788 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2789 if (!fn[0]) fatal("INCLUDE: empty file name");
2791 if (asmTextInclude(fn, system) != 0) fatal("INCLUDE: some shit happens!");
2792 return PI_SKIP_LINE;
2796 ///////////////////////////////////////////////////////////////////////////////
2797 // MODULE, ENDMODULE
2799 static int piENDMODULE (void) {
2800 if (!curModule) fatal("ENDMODULE without MODULE");
2801 if (currLine[0]) {
2802 char *mn = getOneLabelArg();
2803 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
2805 curModule = NULL;
2806 return PI_SKIP_LINE;
2810 static int piMODULE (void) {
2811 ModuleInfo *mi;
2812 char *mn;
2813 SourceLine *ol = curSrcLine;
2814 int inum;
2816 if (curModule) fatal("no nested modules allowed");
2817 mn = getOneLabelArg();
2818 if (!urasm_is_valid_name(mn)) fatal("invalid module name: %s", mn);
2819 mi = moduleFind(mn);
2820 //printf("[%s] %p\n", mn, mi);
2821 if (mi) {
2822 if (mi->seen) {
2823 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
2824 /* skip module */
2825 nextSrcLine(); /* skip "MODULE" line */
2826 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
2827 setCurSrcLine(ol);
2828 fatal("no ENDMODULE");
2830 if (inum == 0) fatal("no nested modules allowed");
2831 curModule = mi;
2832 //skipInstruction(); //k8:wtf?!
2833 return piENDMODULE();
2835 } else {
2836 mi = moduleAdd(mn, curSrcLine->fname);
2838 mi->seen = 1;
2839 curModule = mi;
2840 return PI_SKIP_LINE;
2844 /* Z80, Z80N */
2845 static int piMODEL (void) {
2846 char *mn = getOneIdArgLo();
2847 if (strSkipSpaces(currLine)[0]) fatal("only one model name expected");
2848 if (strcmp(mn, "z80") == 0) urasm_allow_zxnext = 0;
2849 else if (strcmp(mn, "z80a") == 0) urasm_allow_zxnext = 0;
2850 else if (strcmp(mn, "z80n") == 0) urasm_allow_zxnext = 1;
2851 else if (strcmp(mn, "z80next") == 0) urasm_allow_zxnext = 1;
2852 else if (strcmp(mn, "zxnext") == 0) urasm_allow_zxnext = 1;
2853 else fatal("invalid model name: %s", mn);
2854 return PI_SKIP_LINE;
2858 ///////////////////////////////////////////////////////////////////////////////
2859 // DUP, EDUP
2861 static int piEDUP (void) {
2862 fatal("EDUP without DUP");
2863 checkOperatorEnd();
2864 return PI_SKIP_LINE;
2868 static int piDUP (void) {
2869 int defined = 1, dupCnt = 1, inum;
2870 SourceLine *stline, *eline = NULL;
2871 int32_t cnt = getOneExprArg(&defined, NULL);
2873 if (!defined) fatal("DUP: counter must be defined");
2874 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
2875 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
2876 // now find corresponding EDUP
2877 // note that we should skip nested DUPs
2878 nextSrcLine(); // skip ourself
2879 stline = curSrcLine;
2880 while (curSrcLine) {
2881 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
2882 // ok, we found something; what is it?
2883 if (inum == 0) {
2884 // new DUP, skip it
2885 ++dupCnt;
2886 nextSrcLine(); // skip DUP
2887 } else {
2888 // EDUP
2889 if (--dupCnt == 0) {
2890 // gotcha!
2891 eline = curSrcLine;
2892 break;
2894 nextSrcLine(); // skip EDUP
2897 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
2898 // now repeat that lines
2899 while (cnt-- > 0) {
2900 setCurSrcLine(stline);
2901 while (curSrcLine != eline) processCurrentLine();
2903 return PI_SKIP_LINE;
2907 ///////////////////////////////////////////////////////////////////////////////
2908 // IF, ENDIF
2910 static int ifCount = 0;
2913 // results:
2914 // -1: error (should not happen)
2915 // 0: successfully complete
2916 // 1: stopped *AT* "ELSE"
2917 // 2: stopped *AT* "ELSEIF"
2918 static int ifSkipToEndIfOrElse (int wholeBody) {
2919 int inum, wasElse = 0;
2920 SourceLine *oline;
2922 while (curSrcLine) {
2923 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
2924 switch (inum) {
2925 case 0: /* if */
2926 case 6: /* ifx */
2927 nextSrcLine(); // skip IF
2928 ifSkipToEndIfOrElse(1); // and recurse
2929 nextSrcLine(); // skip ENDIF
2930 break;
2931 case 1: /* else */
2932 if (wasElse) fatal("duplicate ELSE");
2933 if (!wholeBody) return 1;
2934 wasElse = 1;
2935 nextSrcLine(); // skip ELSE
2936 break; // and continue
2937 case 2: /* endif */
2938 return 0; // and exit
2939 case 3: /* elif */
2940 case 7: /* elifx */
2941 if (wasElse) fatal("ELSEIF in ELSE");
2942 if (!wholeBody) return 2;
2943 nextSrcLine(); // skip ELSEIF
2944 break; // and continue
2945 case 4: /* macro */
2946 // skip it as a whole
2947 nextSrcLine(); // skip MACRO
2948 for (;;) {
2949 oline = curSrcLine;
2950 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
2951 if (inum == 1) break;
2952 fatal("invalid nested MACRO");
2954 nextSrcLine(); // skip ENDM
2955 break;
2956 case 5: /* endm */
2957 fatal("unexpected ENDM");
2960 fatal("IF without ENDIF");
2961 return -1;
2965 static int piENDIF (void) {
2966 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2967 if (--ifCount < 0) fatal("ENDIF without IF");
2968 checkOperatorEnd();
2969 return PI_SKIP_LINE;
2973 static int piELSE (void) {
2974 if (--ifCount < 0) fatal("ELSE without IF");
2975 nextSrcLine(); // skip ELSE
2976 ifSkipToEndIfOrElse(1);
2977 return PI_SKIP_LINE;
2980 static int piELSEIF (void) {
2981 if (--ifCount < 0) fatal("ELSEIF without IF");
2982 nextSrcLine(); // skip ELSEIF
2983 ifSkipToEndIfOrElse(1);
2984 return PI_SKIP_LINE;
2988 static int piELSEIFX (void) {
2989 if (--ifCount < 0) fatal("ELSEIFX without IF");
2990 nextSrcLine(); // skip ELSEIFX
2991 ifSkipToEndIfOrElse(1);
2992 return PI_SKIP_LINE;
2996 static int piIFAll (int isIfX) {
2997 int defined = 1;
2998 int ooo = lblOptMakeU2;
2999 lblOptMakeU2 = (isIfX ? 1 : 0);
3000 int32_t cond = getOneExprArg(&defined, NULL);
3001 lblOptMakeU2 = ooo;
3003 if (!defined) {
3004 if (!isIfX) fatal("IF: condition must be defined");
3005 cond = 0; // for IFX: 0 if there is any undefined label
3007 if (cond) {
3008 // ok, do it until ELSE/ELSEIF/ENDIF
3009 ++ifCount;
3010 return PI_SKIP_LINE;
3012 for (;;) {
3013 int r;
3014 char *args;
3016 nextSrcLine(); // skip last instruction
3017 // skip until ELSE/ELSEIF/ENDIF
3018 r = ifSkipToEndIfOrElse(0);
3019 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
3020 if (r == 0) break; // ENDIF
3021 if (r == 1) { ++ifCount; break; } // ELSE
3022 // ELSEIF, do condition
3023 args = strIsCommand("ELSEIF", currLine);
3024 if (args) {
3025 isIfX = 0;
3026 } else {
3027 if ((args = strIsCommand("ELSEIFX", currLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
3028 isIfX = 1;
3030 memmove(currLine, args, strlen(args)+1);
3031 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
3032 cond = getOneExprArg(&defined, NULL);
3033 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
3034 if (!defined) {
3035 if (!isIfX) fatal("ELSEIF: condition must be defined");
3036 cond = 0; // for IFX: 0 if there is any undefined label
3038 if (cond) { ++ifCount; break; } // condition is true
3040 return PI_SKIP_LINE;
3044 static int piIF (void) { return piIFAll(0); }
3045 static int piIFX (void) { return piIFAll(1); }
3048 ///////////////////////////////////////////////////////////////////////////////
3049 // macro processor
3050 ///////////////////////////////////////////////////////////////////////////////
3052 what i did with MACRO is the brain-damaged cheating all the way.
3054 first, i will collect the MACRO body and remember it (removing it
3055 from the main source code, so second pass will not see it).
3057 second, when the macro is used, i will:
3058 * insert the whole macro body in place (label resolver will
3059 fix "..lbl" labels for us)
3060 * let the asm play with it
3062 this is not the best scheme, but it is fairly simple and it works.
3065 // will be called when parser encounters term starting with '=' or '*'
3066 // first term char will not be skipped
3067 // must return pointer to the first char after expression end
3068 static const char *getValueCB (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3069 char name[257];
3070 char *p = name;
3072 if (curmacro == NULL) fatal("'=' outside of macro");
3073 if (*expr++ != '=') fatal("'=' expected!");
3075 expr = strSkipSpaces(expr);
3076 while (*expr) {
3077 if (isAlphaDigit(*expr) || *expr == '_') {
3078 if (p-name > 250) fatal("id too long");
3079 *p++ = *expr++;
3080 continue;
3082 break;
3084 *p = 0;
3086 expr = strSkipSpaces(expr);
3087 for (int f = 0; f < curmacro->mac->argc; ++f) {
3088 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3089 if (*expr == '[') {
3090 urasm_exprval_t v;
3091 int l = (int)strlen(curmacro->argvals[f]);
3092 ++expr; // skip "["
3093 urasm_exprval_init(&v);
3094 expr = urasm_expr_ex(&v, expr, addr, &donteval, defined, error);
3095 if (*error) return expr;
3096 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
3097 ++expr;
3098 if (v.val < 0) v.val += l;
3099 if (v.val < 0 || v.val >= l) {
3100 res->val = '?';
3101 } else {
3102 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
3104 return expr;
3105 } else {
3106 urasm_expr_ex(res, curmacro->argvals[f], addr, &donteval, defined, error);
3107 return expr;
3112 fatal("unknown macro variable: '%s'", name);
3116 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
3117 // opr starts with '=' (invariant)
3118 static int expandCB (char *opr, int oprlen) {
3119 char name[257], *p = name, *op = opr;
3121 if (curmacro == NULL) fatal("'=' outside of macro");
3122 if (*op++ != '=') fatal("'=' expected!"); // just in case
3123 //fprintf(stderr, "expand: [%s]\n", opr);
3125 if (!isAlpha(op[0])) return 0; // nothing to do
3127 // copy argument name
3128 while (*op) {
3129 if (isAlphaDigit(*op) || *op == '_') {
3130 if (p-name > 250) fatal("id too long");
3131 *p++ = *op++;
3132 continue;
3134 break;
3136 *p = 0;
3138 // expand argument? we only need to expand `=arg[n]`
3139 op = strSkipSpaces(op);
3140 if (op[0] != '[') return 0;
3142 for (int f = 0; f < curmacro->mac->argc; ++f) {
3143 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3144 if (*op != '[') abort(); // assertion, just in case
3145 // replace argument with character, or with literal value
3146 // used for `=regpair[0]` or `=regpair[]`
3147 if (op[1] == ']') {
3148 op += 2;
3149 const int l = (int)strlen(curmacro->argvals[f]);
3150 const int lleft = (int)strlen(op);
3151 char *tmp = malloc(l+lleft+8);
3152 snprintf(tmp, l+lleft+8, "%s%s", curmacro->argvals[f], op);
3153 if ((int)strlen(tmp) > oprlen) {
3154 free(tmp);
3155 return -1;
3157 strcpy(opr, tmp);
3158 free(tmp);
3159 return 0;
3160 } else {
3161 urasm_exprval_t v;
3162 int error = 0, defined = 1, donteval = 0;
3163 ++op; // skip '['
3164 urasm_exprval_init(&v);
3165 op = (char *)urasm_expr_ex(&v, op, pc, &donteval, &defined, &error);
3166 if (error) return -1;
3167 // result should be a number
3168 if (op == NULL || *op != ']' || v.str != NULL) return -1;
3169 ++op; // skip ']'
3170 // it is guaranteed to have more than one char in opr
3171 // so we can simply put char from argument value, and remove other expression chars
3172 const int l = (int)strlen(curmacro->argvals[f]);
3173 if (v.val < 0) v.val += l; // negative: indexing from the end
3174 if (v.val < 0 || v.val >= l) fatal("index %d is out of bounds for macro argument '%s'", v.val, curmacro->mac->argnames[f]);
3175 // copy char
3176 opr[0] = curmacro->argvals[f][v.val];
3177 // remove other chars
3178 memmove(opr+1, op, strlen(op)+1);
3180 return 0;
3184 fatal("unknown macro variable: '%s'", name);
3188 // main macro expander
3189 static void processMacro (MacroDef *mc) {
3190 SourceLine *oldcurline = curSrcLine;
3191 CurMacroDef cm;
3193 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3194 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3196 // parse macro arguments
3197 cm.mac = mc;
3198 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) cm.argvals[f] = NULL;
3199 int currArg = 0;
3200 for (;;) {
3201 removeSpaces();
3202 // do we have more arguments?
3203 if (!currLine[0]) break;
3204 // check for argument name (this is purely cosmetic thing)
3205 // use like this: "mcall :argname=[value]" (value may be omited for default)
3206 if (currLine[0] == ':' && isAlpha(currLine[1])) {
3207 int pos = 2;
3208 while (isAlphaDigit(currLine[pos]) || currLine[pos] == '_') ++pos;
3209 //hack!
3210 const char svch = currLine[pos];
3211 currLine[pos] = 0;
3212 if (strcasecmp(currLine+1, mc->argnames[currArg]) != 0) {
3213 // out-of-order, find proper argument and rewind to it
3214 currArg = -1;
3215 for (int c = 0; c < mc->argc; ++c) {
3216 if (strcasecmp(currLine+1, mc->argnames[c]) == 0) {
3217 currArg = c;
3218 break;
3221 if (currArg < 0) fatal("macro '%s' has no argument named '%s'", mc->name, currLine+1);
3223 currLine[pos] = svch;
3224 // remove argument name
3225 memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
3226 removeSpaces();
3227 // check for '='
3228 if (currLine[0] != '=') fatal("expected '=' after argument name");
3229 // remove '='
3230 memmove(currLine, currLine+1, strlen(currLine));
3231 removeSpaces();
3233 // too many args?
3234 if (currArg >= mc->argc) fatal("too many arguments to macro '%s'", mc->name);
3235 // check for default value
3236 if (currLine[0] == ',') {
3237 if (mc->argdefaults[currArg] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[currArg]);
3238 cm.argvals[currArg] = strdup(mc->argdefaults[currArg]);
3239 memmove(currLine, currLine+1, strlen(currLine));
3240 } else {
3241 // skip argument (so we will know its length, and will be able to copy it)
3242 char *e = skipMacroArg(currLine);
3243 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3244 const char ech = *e;
3245 if (ech != 0) {
3246 if (ech == ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc->name);
3247 if (ech != ',') fatal("invalid invocation of macro '%s'", mc->name);
3249 *e = 0;
3250 cm.argvals[currArg] = strdup(currLine);
3251 // strip trailing spaces
3252 strTrimRight(cm.argvals[currArg]);
3253 if (ech) {
3254 *e = ech;
3255 memmove(currLine, e+1, strlen(e));
3256 } else {
3257 currLine[0] = 0;
3260 ++currArg;
3262 // check for line end
3263 removeSpaces();
3264 if (currLine[0]) fatal("invalid macro invocation");
3266 // setup default argument values
3267 for (int f = 0; f < mc->argc; ++f) {
3268 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
3269 if (cm.argvals[f]) continue;
3270 if (mc->argdefaults[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
3271 cm.argvals[f] = strdup(mc->argdefaults[f]);
3272 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
3275 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3277 // insert macro code into the source
3278 setCurSrcLine(mc->lines);
3279 curmacro = &cm;
3280 ++curmacronum;
3281 while (curSrcLine != NULL) {
3282 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3283 processCurrentLine();
3286 setCurSrcLine(oldcurline);
3287 curmacro = NULL;
3288 nextSrcLine();
3292 static int piMACRO (void) {
3293 char *name;
3294 int argc = 0;
3295 char *argdefaults[MAX_MACRO_ARGS];
3296 char *argnames[MAX_MACRO_ARGS];
3297 SourceLine *stline, *eline = NULL;
3298 MacroDef *mc;
3300 name = strdup(getLabelArg(0));
3301 //fprintf(stderr, "[%s]\n", name);
3302 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
3303 if (currLine[0] == ',') memmove(currLine, currLine+1, strlen(currLine));
3304 removeSpaces();
3306 while (currLine[0]) {
3307 if (argc >= MAX_MACRO_ARGS) fatal("too many arguments in MACRO");
3308 if (!isAlpha(currLine[0])) fatal("invalid MACRO definition");
3309 argnames[argc] = strdup(getLabelArg(0));
3310 if (currLine[0] == '=') {
3311 // default value
3312 char *e = strchr(currLine, ','), tch;
3314 if (e == NULL) e = currLine+strlen(currLine);
3315 tch = *e;
3316 *e = 0;
3317 argdefaults[argc] = strdup(currLine+1);
3318 *e = tch;
3319 memmove(currLine, e, strlen(e)+1);
3320 } else {
3321 argdefaults[argc] = NULL;
3323 removeSpaces();
3324 if (currLine[0] == ',') {
3325 memmove(currLine, currLine+1, strlen(currLine));
3326 removeSpaces();
3328 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3329 ++argc;
3332 // now find corresponding ENDM
3333 // note that we should skip nested DUPs
3334 stline = curSrcLine;
3335 nextSrcLine(); // skip ourself
3336 while (curSrcLine) {
3337 int inum;
3339 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
3340 // ok, we found something; what is it?
3341 if (inum == 0) {
3342 // new MACRO
3343 fatal("no nested MACROs yet");
3344 } else {
3345 // ENDM, gotcha!
3346 eline = curSrcLine;
3347 // kill ENDM
3348 eline->line[0] = 0;
3349 nextSrcLine(); // skip ENDM
3350 break;
3353 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
3355 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
3356 mc->name = name;
3357 mc->argc = argc;
3358 for (int f = 0; f < argc; ++f) { mc->argdefaults[f] = argdefaults[f]; mc->argnames[f] = argnames[f]; }
3360 eline->next = NULL;
3361 mc->lines = stline->next;
3362 stline->next = curSrcLine;
3363 stline->line[0] = 0;
3365 mc->next = maclist;
3366 maclist = mc;
3367 setCurSrcLine(stline);
3368 return PI_SKIP_LINE;
3372 static int piENDM (void) {
3373 fatal("ENDM without MACRO");
3374 return PI_SKIP_LINE;
3378 ///////////////////////////////////////////////////////////////////////////////
3379 // line processor
3380 static int optWriteType = 't';
3381 static int optWTChanged = 0;
3384 static void piTapParseLoaderName (void) {
3385 if (eatComma()) {
3386 int len;
3387 if (!isStrArg()) fatal("loader name expected");
3388 char *fn = getStrArg(&len);
3389 if (len > 10) fatal("loader name too long");
3390 memset(tapeLoaderName, ' ', 10);
3391 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
3396 static int piMATHMODE (void) {
3397 char *name = getOneLabelArg();
3398 if (strcasecmp(name, "OLD") == 0) urasm_use_old_priorities = 1;
3399 else if (strcasecmp(name, "NEW") == 0) urasm_use_old_priorities = 0;
3400 else fatal("invalid math mode; NEW or OLD expected");
3401 return PI_SKIP_LINE;
3405 static int piDEFFMT (void) {
3406 char *name;
3408 if (isStrArg()) {
3409 int len = 0;
3410 name = getStrArg(&len);
3411 } else {
3412 name = getLabelArg(1);
3414 if (optWTChanged) return 1;
3416 optRunDMB = optRunTape = optRunSCL = 0;
3417 //optRunSNA = 0;
3419 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA") || !strcasecmp(name, "SNARUN")) {
3420 optWriteType = 's';
3421 if (currLine[0]) fatal("too many expressions");
3422 return PI_SKIP_LINE;
3424 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
3425 optWriteType = 't';
3426 piTapParseLoaderName();
3427 return PI_SKIP_LINE;
3429 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE") || !strcasecmp(name, "TAPERUN")) {
3430 optRunTape = 1;
3431 optWriteType = 't';
3432 piTapParseLoaderName();
3433 return PI_SKIP_LINE;
3435 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
3436 optWriteType = 'r';
3437 if (currLine[0]) fatal("too many expressions");
3438 return PI_SKIP_LINE;
3440 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB") || !strcasecmp(name, "DMBRUN")) {
3441 optRunDMB = (name[3] != 0);
3442 optWriteType = 'd';
3443 if (currLine[0]) fatal("too many expressions");
3444 return PI_SKIP_LINE;
3446 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
3447 optWriteType = 'n';
3448 if (currLine[0]) fatal("too many expressions");
3449 return PI_SKIP_LINE;
3451 if (!strcasecmp(name, "SCL") || !strcasecmp(name, "RUNSCL") || !strcasecmp(name, "SCLRUN")) {
3452 optWriteType = 'S';
3453 optRunSCL = (name[3] != 0 ? -1 : 0); /* no boot */
3454 piTapParseLoaderName();
3455 return PI_SKIP_LINE;
3457 if (!strcasecmp(name, "SCLBOOT") || !strcasecmp(name, "BOOTSCL")) {
3458 optWriteType = 'S';
3459 optRunSCL = 1; /* with boot */
3460 piTapParseLoaderName();
3461 return PI_SKIP_LINE;
3463 fatal("invalid default output type: %s", name);
3467 ///////////////////////////////////////////////////////////////////////////////
3468 // line processor
3470 static void processCurrentLine (void) {
3471 if (!curSrcLine) return; // do nothing
3472 loadCurSrcLine();
3473 if (asmMode == AMODE_FWORD) {
3474 processForthWordLine();
3475 nextSrcLine(); // skip it
3476 return;
3478 processLabel();
3479 for (;;) {
3480 char *str, *ee, name[66];
3481 UrAsmOp *op;
3482 int len;
3483 const char *errpos;
3485 removeSpacesAndColons();
3486 // skip spaces and ':'
3487 if (!currLine[0]) { nextSrcLine(); break; }
3488 // try to find and process command
3489 str = currLine; //while (*str && isSpace(*str)) ++str; // skip spaces
3490 // find command end
3491 for (ee = str; *ee && !isSpace(*ee) && *ee != '"' && *ee != '\'' && *ee != ',' && *ee != ':'; ++ee) {}
3492 // get command, if any
3493 if (ee != str && ee-str <= 64) {
3494 MacroDef *mc;
3495 memset(name, 0, sizeof(name));
3496 memmove(name, str, ee-str);
3497 /* known command? */
3498 op = urFindOp(name);
3499 if (op) {
3500 // ok, do it
3501 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3502 memmove(currLine, str, strlen(str)+1);
3503 if (op->fn()) {
3504 nextSrcLine(); // skip it
3505 break;
3507 continue;
3509 /* known macro? */
3510 if ((mc = findMacro(name)) != NULL) {
3511 // ok, do it
3512 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3513 memmove(currLine, str, strlen(str)+1);
3514 processMacro(mc);
3515 /* only one macro per line! */
3516 break;
3520 len = urasm_opasm(currLine, pc, disp, &errpos);
3521 if (len < 0) fatalUrLib(len);
3522 pc += len;
3523 disp += len;
3524 if (len >= 0 && errpos) {
3525 memmove(currLine, errpos+1, strlen(errpos));
3526 } else {
3527 nextSrcLine(); // skip it
3528 break;
3534 ///////////////////////////////////////////////////////////////////////////////
3535 // forth instructions
3537 static uint16_t forthLatest = 0;
3538 static const char *forthCurrWord = NULL;
3541 typedef struct ForthWordT {
3542 char *name; // uppercased
3543 uint16_t nfa;
3544 uint16_t cfa;
3545 int isbranch;
3546 struct ForthWordT *next;
3547 } ForthWord;
3549 static ForthWord *forthWordList = NULL;
3550 static ForthWord *forthWordListTail = NULL;
3552 static ForthWord *findForthWord (const char *name) {
3553 if (!name) name = "";
3554 for (ForthWord *w = forthWordList; w; w = w->next) {
3555 if (strcasecmp(name, w->name) == 0) return w;
3557 return NULL;
3561 static ForthWord *addForthWord (const char *name) {
3562 if (!name) name = "";
3563 ForthWord *nw = findForthWord(name);
3564 if (pass == 0) {
3565 if (nw) fatal("duplicate forth word: '%s'", name);
3566 nw = malloc(sizeof(ForthWord));
3567 nw->name = strdup(name);
3568 for (char *s = nw->name; *s; ++s) {
3569 if (s[0] >= 'a' && s[0] <= 'z') s[0] = s[0]-'a'+'A';
3571 nw->nfa = 0;
3572 nw->cfa = 0;
3573 nw->isbranch = 0;
3574 nw->next = NULL;
3575 if (forthWordListTail) forthWordListTail->next = nw; else forthWordList = nw;
3576 forthWordListTail = nw;
3577 } else {
3578 if (!nw) fatal("internal compiler error");
3580 return nw;
3585 ;; word format:
3586 ;; db len_flags
3587 ;; name
3588 ;; dw lfa
3589 ;; dw cfa
3590 ;; ....
3591 ;; len_flags:
3592 ;; bits 0-4: length
3593 ;; bit 5: SMUDGE flag (=1: word definition isn't finished)
3594 ;; bit 6: IMMEDIATE flag (=1: true)
3595 ;; bit 7: always 1
3596 ;; the last byte of the name always has bit 7 set
3598 static ForthWord *forthWordHead (const char *name, int imm) {
3599 ForthWord *nw = addForthWord(name);
3600 if (!name) name = "";
3601 const uint16_t lt = disp;
3602 //fprintf(stderr, "#%04X: fw=<%s>\n", lt, name);
3603 if (pass == 0) {
3604 nw->nfa = disp;
3605 } else {
3606 if (nw->nfa != disp) fatal("forth word `%s`: orig NFA=#%04X; pass1 NFA=#%04X\n", name, nw->nfa, disp);
3607 //fprintf(stderr, "#%04X: fw=<%s>\n", lt, name);
3609 size_t nlen = strlen(name);
3610 if (nlen == 0) nlen = 1;
3611 if (nlen > 31) fatal("forth word name too long: '%s'", name);
3612 // word length and flags (nfa)
3613 emitByte((nlen&0xff)|0x80u|(imm ? 0x40u : 0x00u));
3614 while (nlen != 0) {
3615 uint8_t b = (uint8_t)(name[0]&0xffu);
3616 if (b >= 'a' && b <= 'z') b = b-'a'+'A';
3617 if (nlen == 1) b |= 0x80u;
3618 emitByte(b);
3619 ++name;
3620 --nlen;
3622 // lfa
3623 emitWord(forthLatest);
3624 forthLatest = lt;
3625 nw->cfa = disp;
3626 //fprintf(stderr, "#%04X: fw=<%s>\n", nw->cfa, nw->name);
3627 return nw;
3631 /* returns static buffer */
3633 static char *forthPeekWord (void) {
3634 static char buf[256];
3635 size_t pos = 0;
3636 while (isSpace(currLine[pos])) ++pos;
3637 size_t bp = 0;
3638 while (currLine[pos] && !isSpace(currLine[pos])) {
3639 if (bp >= sizeof(buf)-2) fatal("forth word too long");
3640 buf[bp++] = currLine[pos++];
3642 buf[bp] = 0;
3643 return (buf[0] ? buf : NULL);
3648 /* returns static buffer */
3649 static char *forthGetWord (void) {
3650 static char buf[256];
3651 size_t pos = 0;
3652 while (isSpace(currLine[pos])) ++pos;
3653 size_t bp = 0;
3654 while (currLine[pos] && !isSpace(currLine[pos])) {
3655 if (bp >= sizeof(buf)-2) fatal("forth word too long");
3656 buf[bp++] = currLine[pos++];
3658 buf[bp] = 0;
3659 while (isSpace(currLine[pos])) ++pos;
3660 if (pos > 0) memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
3661 return (buf[0] ? buf : NULL);
3665 static void forthEmitLabel (const char *name) {
3666 UrLabelInfo *lbl = urFindLabel(name);
3667 if (!lbl) {
3668 if (pass != 0) fatal("using undefined label '%s'", name);
3669 lbl = urAddLabel(name);
3670 //fprintf(stderr, "****** <%s>\n", lbl->name);
3671 lbl->type = (lblOptMakeU2 ? -42 : -1);
3672 lbl->known = 0;
3673 lbl->refLine = curSrcLine->lineNo;
3674 lbl->refFile = strdup(curSrcLine->fname);
3676 emitWord(lbl->value);
3680 static void forthDoConstVar (const char *lbl) {
3681 const char *wname = forthGetWord();
3682 if (!wname) fatal("forth word name expected");
3683 /*ForthWord *nw =*/ forthWordHead(wname, 0);
3684 // cfa
3685 forthEmitLabel(lbl);
3686 // constant value
3687 int defined = 1, addr = 0;
3688 int32_t res = getOneExprArg(&defined, &addr);
3689 if (strcmp(lbl, "_do2var") != 0) {
3690 if (res < 0) res = 65536+res;
3691 emitWord(res&0xffffU);
3692 } else {
3693 // double var
3694 emitWord(res&0xffffU);
3695 emitWord((res>>16)&0xffffU);
3697 //fprintf(stderr, "FORTH CONST/VAR: '%s' = #%04X\n", nw->name, res);
3698 checkOperatorEnd();
3702 static int piForthConst (void) {
3703 forthDoConstVar("_doconst");
3704 return PI_SKIP_LINE;
3708 static int piForthVar (void) {
3709 forthDoConstVar("_dovar");
3710 return PI_SKIP_LINE;
3714 static int piForthDVar (void) {
3715 forthDoConstVar("_do2var");
3716 return PI_SKIP_LINE;
3720 static int piForthUser (void) {
3721 forthDoConstVar("_douser");
3722 return PI_SKIP_LINE;
3726 static int piForthCodeWord (void) {
3727 if (asmMode != AMODE_NORMAL) fatal("invalid forth define");
3728 asmMode = AMODE_FCODE;
3729 const char *wname = forthGetWord();
3730 if (!wname) fatal("forth word name expected");
3731 ForthWord *nw = forthWordHead(wname, 0);
3732 // cfa
3733 emitWord((disp+2)&0xffffU);
3734 wname = forthGetWord();
3735 if (wname) {
3736 if (strcasecmp(wname, "FBRANCH") == 0) nw->isbranch = 1; else fatal("end of line expected");
3738 checkOperatorEnd();
3739 //fprintf(stderr, "FORTH CODE WORD: '%s'\n", nw->name);
3740 forthCurrWord = nw->name;
3741 return PI_SKIP_LINE;
3745 static int piForthCodeWordEnd (void) {
3746 if (asmMode != AMODE_FCODE || !forthCurrWord) fatal("invalid forth define");
3747 asmMode = AMODE_NORMAL;
3748 const char *wname = forthGetWord();
3749 if (!wname) fatal("forth word name expected");
3750 if (!forthWordListTail) fatal("end of unknown forth code word");
3751 if (strcasecmp(wname, forthCurrWord) != 0) fatal("end of forth code word '%s', but expected '%s'", wname, forthWordListTail->name);
3752 checkOperatorEnd();
3753 forthCurrWord = NULL;
3754 return PI_SKIP_LINE;
3758 static int piForthWord (void) {
3759 if (asmMode != AMODE_NORMAL) fatal("invalid forth define");
3760 asmMode = AMODE_FWORD;
3761 const char *wname = forthGetWord();
3762 if (!wname) fatal("forth word name expected");
3763 if (strcmp(wname, "~") == 0) wname = "";
3764 char *fwn = strdup(wname);
3765 wname = forthGetWord();
3766 int imm = 0;
3767 if (wname && strcasecmp(wname, "IMM") == 0) imm = 1;
3768 ForthWord *nw = forthWordHead(fwn, imm);
3769 free(fwn);
3770 // cfa
3771 forthEmitLabel("_doforth");
3772 wname = forthGetWord();
3773 if (wname) {
3774 if (strcasecmp(wname, "FBRANCH") == 0) nw->isbranch = 1; else fatal("end of line expected");
3776 checkOperatorEnd();
3777 //fprintf(stderr, "FORTH WORD: '%s'\n", nw->name);
3778 forthCurrWord = nw->name;
3779 return PI_SKIP_LINE;
3783 static int piForthWordDoes (void) {
3784 if (asmMode != AMODE_NORMAL) fatal("invalid forth define");
3785 // get forth word name
3786 const char *wname = forthGetWord();
3787 if (!wname) fatal("forth word name expected");
3788 if (strcmp(wname, "~") == 0) wname = "";
3789 char *fwn = strdup(wname);
3790 // get does label name
3791 wname = forthGetWord();
3792 if (!wname) fatal("does label expected");
3793 char *dlb = strdup(wname);
3794 // get "imm" flag
3795 wname = forthGetWord();
3796 int imm = 0;
3797 if (wname && strcasecmp(wname, "IMM") == 0) imm = 1;
3798 // nfa
3799 /*ForthWord *nw =*/ forthWordHead(fwn, imm);
3800 free(fwn);
3801 // cfa
3802 forthEmitLabel("_dodoes");
3803 // does label
3804 forthEmitLabel(dlb);
3805 free(dlb);
3806 checkOperatorEnd();
3807 //fprintf(stderr, "FORTH DOES WORD: '%s'\n", nw->name);
3808 return PI_SKIP_LINE;
3812 static int piForthWordEnd (void) {
3813 if (asmMode != AMODE_FWORD || !forthCurrWord) fatal("invalid forth define");
3814 asmMode = AMODE_NORMAL;
3815 const char *wname = forthGetWord();
3816 if (!wname) fatal("forth word name expected");
3817 if (!forthWordListTail) fatal("end of unknown forth code word");
3818 if (strcasecmp(wname, forthCurrWord) != 0) fatal("end of forth code word '%s', but expected '%s'", wname, forthWordListTail->name);
3819 checkOperatorEnd();
3820 forthCurrWord = NULL;
3821 return PI_SKIP_LINE;
3825 static int isStartsWithForthCmd (void) {
3826 return
3827 currLine[0] == '$' &&
3828 toUpper(currLine[1]) == 'F' &&
3829 toUpper(currLine[2]) == 'O' &&
3830 toUpper(currLine[3]) == 'R' &&
3831 toUpper(currLine[4]) == 'T' &&
3832 toUpper(currLine[5]) == 'H';
3836 //HACK!
3838 static int forthIsBranchWord (const char *wname) {
3839 if (!wname || !wname[0]) return 0;
3840 if (strcasestr(wname, "BRANCH")) return 1;
3841 if (strcasestr(wname, "LOOP)")) return 1;
3842 return 0;
3847 static void processForthWordLine (void) {
3848 // check for "$FORTH..."
3849 if (isStartsWithForthCmd()) {
3850 const char *w = forthGetWord();
3851 if (!w) fatal("wtf?!");
3852 if (asmMode != AMODE_FWORD) fatal("wtf1?!");
3853 if (strcasecmp(w, "$FORTH_END_WORD") != 0) fatal("invalid forth instruction");
3854 w = forthGetWord();
3855 if (!w) fatal("forth word name expected");
3856 if (strcmp(w, "~") == 0) w = "";
3857 asmMode = AMODE_NORMAL;
3858 if (!forthCurrWord) fatal("end of unknown forth code word");
3859 if (strcasecmp(w, forthCurrWord) != 0) fatal("end of forth code word '%s', but expected '%s'", w, forthWordListTail->name);
3860 checkOperatorEnd();
3861 forthCurrWord = NULL;
3862 return;
3864 // check for a label
3865 if (isAlpha(currLine[0]) || currLine[0] == '_') {
3866 size_t lend = 1;
3867 while (currLine[lend] && currLine[lend] != ':') {
3868 //fprintf(stderr, "lend=%u (%c)\n", lend, currLine[lend]);
3869 if (isSpace(currLine[lend])) fatal("invalid label");
3870 ++lend;
3872 if (lend > 64) fatal("label too long");
3873 //fprintf(stderr, "lend=%u (%c)\n", lend, currLine[lend]);
3874 if (currLine[lend] != ':') fatal("label expected");
3875 char nn[68];
3876 memcpy(nn, currLine, lend);
3877 nn[lend] = 0;
3878 ++lend;
3879 memmove(currLine, currLine+lend, strlen(currLine+lend)+1);
3880 UrLabelInfo *lbl = urAddLabel(nn);
3881 if (!lbl->refFile) {
3882 lbl->refLine = curSrcLine->lineNo;
3883 lbl->refFile = strdup(curSrcLine->fname);
3885 if (pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
3886 // code label
3887 lbl->type = 2;
3888 lbl->value = disp;
3889 lbl->known = 1;
3890 lbl->fixuptype = UR_FIXUP_NONE;
3891 //fprintf(stderr, "flabel: %s (#%04X)\n", lbl->name, lbl->value);
3894 //fprintf(stderr, "<: %s :>\n", currLine);
3895 for (;;) {
3896 removeSpaces();
3897 const char *wname = forthGetWord();
3898 if (!wname) break;
3899 //fprintf(stderr, " %s: #%04X: <%s>\n", forthCurrWord, disp, wname);
3901 // check for string
3902 if (strcmp(wname, "(.\")") == 0 || strcmp(wname, "(\")") == 0) {
3903 if (pass == 0) {
3904 emitWord(0);
3905 } else {
3906 ForthWord *fw = findForthWord(wname);
3907 if (!fw) fatal("forth word `%s` not found", wname);
3908 emitWord(fw->cfa);
3910 removeSpaces();
3911 char qch = currLine[0];
3912 if (qch != '~' && qch != '"' && qch != '\'') fatal("invalid string quotation");
3913 memmove(currLine, currLine+1, strlen(currLine+1)+1);
3914 size_t pos = 0;
3915 while (currLine[pos] && currLine[pos] != qch) {
3916 if (currLine[pos] != '\\') { ++pos; continue; }
3917 int ch = -1;
3918 if (currLine[pos+1] == qch) {
3919 ch = qch;
3920 } else {
3921 switch (currLine[pos+1]) {
3922 case 'r': ch = 13; break;
3923 case 'n': ch = 256; break;
3924 case 't': ch = 9; break;
3925 case 'b': ch = 8; break;
3926 case 'v': ch = 10; break;
3927 case '\\': ch = '\\'; break;
3928 case 'x':
3929 //fprintf(stderr, "<%s>\n", currLine);
3930 if (!isHexDigit(currLine[pos+2]) || !isHexDigit(currLine[pos+3])) fatal("invalid hex escape");
3931 ch = digitInBase(currLine[pos+2], 16)*16+digitInBase(currLine[pos+3], 16);
3932 currLine[pos++] = ch;
3933 memmove(currLine+pos, currLine+pos+3, strlen(currLine+pos+3)+1);
3934 ch = 666; // special
3935 break;
3938 if (ch < 0) fatal("invalid escape");
3939 if (ch == 666) continue;
3940 if (ch == 256) {
3941 // cr+lf
3942 currLine[pos++] = 13;
3943 currLine[pos++] = 10;
3944 } else {
3945 currLine[pos++] = ch;
3946 memmove(currLine+pos, currLine+pos+1, strlen(currLine+pos+1)+1);
3949 if (pos > 255) fatal("string too long");
3950 if (currLine[pos] != qch) fatal("unterminated string");
3951 emitByte(pos&0xffU);
3952 for (size_t f = 0; f < pos; ++f) emitByte((uint8_t)(currLine[f]&0xffU));
3953 ++pos;
3954 memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
3955 continue;
3958 // check for numeric literal
3959 if (strcasecmp(wname, "LIT") == 0) {
3960 if (pass == 0) {
3961 emitWord(0);
3962 } else {
3963 ForthWord *fw = findForthWord(wname);
3964 if (!fw) fatal("forth word `%s` not found", wname);
3965 emitWord(fw->cfa);
3967 removeSpaces();
3968 wname = forthGetWord();
3969 if (!wname) fatal("unexpected end of line");
3970 //fprintf(stderr, " %s: #%04X: <%s>\n", forthCurrWord, disp, wname);
3971 if (pass == 0) {
3972 emitWord(0);
3973 } else {
3974 // check for label
3975 UrLabelInfo *lbl = urFindLabel(wname);
3976 if (lbl) {
3977 if (!lbl->known) fatal("unknown label '%s'", lbl->name);
3978 emitWord(lbl->value&0xffffU);
3979 continue;
3981 // check for number
3982 char *end;
3983 int base = 10;
3984 if (wname[0] == '#' && isHexDigit(wname[1])) {
3985 ++wname;
3986 base = 16;
3988 long int v = strtol(wname, &end, base);
3989 if (end[0] == 0 && end != wname) {
3990 if (v < -32768 || v > 65535) fatal("forth numeric literal out of range: %d\n", (int)v);
3991 if (v < 0) v = 65536+v;
3992 emitWord(v&0xffffU);
3993 continue;
3995 fatal("number expected, got `%s`", wname);
3997 continue;
4000 // check for `COMPILE`
4001 if (strcasecmp(wname, "COMPILE") == 0) {
4002 if (pass == 0) {
4003 emitWord(0);
4004 } else {
4005 ForthWord *fw = findForthWord(wname);
4006 if (!fw) fatal("forth word `%s` not found", wname);
4007 emitWord(fw->cfa);
4009 removeSpaces();
4010 wname = forthGetWord();
4011 if (!wname) fatal("unexpected end of line");
4012 if (pass == 0) {
4013 emitWord(0);
4014 } else {
4015 ForthWord *fw = findForthWord(wname);
4016 if (!fw) fatal("forth word `%s` not found", wname);
4017 emitWord(fw->cfa);
4019 continue;
4022 // other words
4023 if (pass == 0) {
4024 emitWord(0);
4025 } else {
4026 if (strcmp(wname, "~") == 0) wname = "";
4027 ForthWord *fw = findForthWord(wname);
4028 if (!fw) {
4029 // check for label
4030 UrLabelInfo *lbl = urFindLabel(wname);
4031 if (lbl) {
4032 fatal("label '%s' without LIT", lbl->name);
4033 emitWord(lbl->value&0xffffU);
4035 // check for number
4036 char *end;
4037 int base = 10;
4038 if (wname[0] == '#' && isHexDigit(wname[1])) {
4039 ++wname;
4040 base = 16;
4042 long int v = strtol(wname, &end, base);
4043 if (end[0] == 0 && end != wname) {
4044 fatal("number without LIT! (%d)", (int)v);
4045 continue;
4047 fatal("unknown forth word '%s'", wname);
4049 emitWord(fw->cfa);
4050 if (fw->isbranch) {
4051 wname = forthGetWord();
4052 if (!wname) fatal("label expected");
4053 UrLabelInfo *lbl = urFindLabel(wname);
4054 if (!lbl) fatal("unknown label in forth code: '%s'", wname);
4055 if (!lbl->known) fatal("undefined label '%s'", lbl->name);
4056 if (lbl->value < 0 || lbl->value > 65535) fatal("invalid jump label in forth code: '%s' (%d)", wname, lbl->value);
4057 int v = lbl->value-(int)disp;
4058 if (v < -32767 || v > 32767) fatal("forth jump too far");
4059 if (v < 0) v = 65536+v;
4060 emitWord(v&0xffffU);
4067 ///////////////////////////////////////////////////////////////////////////////
4068 // setup instructions
4070 static void registerInstructions (void) {
4071 urAddOp("DISPLAY", piDISPLAY);
4072 urAddOp("DISPLAY0", piDISPLAY0);
4073 urAddOp("DISPLAYA", piDISPLAYA);
4074 urAddOp("DISPHEX", piDISPHEX);
4075 urAddOp("DISPHEX0", piDISPHEX0);
4076 urAddOp("DISPHEXA", piDISPHEXA);
4078 urAddOp("DEFFMT", piDEFFMT);
4079 urAddOp("$MODEL", piMODEL);
4081 urAddOp("MACRO", piMACRO);
4082 urAddOp("ENDM", piENDM);
4084 urAddOp("ORG", piORG);
4085 urAddOp("DISP", piDISP);
4086 urAddOp("ENDDISP", piENDDISP);
4087 urAddOp("PHASE", piDISP);
4088 urAddOp("DEPHASE", piENDDISP);
4089 urAddOp("UNPHASE", piENDDISP);
4090 urAddOp("ALIGN", piALIGN);
4091 urAddOp("DISPALIGN", piDISPALIGN);
4092 urAddOp("PHASEALIGN", piDISPALIGN);
4093 urAddOp("ENT", piENT);
4094 urAddOp("CLR", piCLR);
4095 urAddOp("RESERVE", piRESERVE);
4097 urAddOp("INCLUDE", piINCLUDE);
4098 urAddOp("INCBIN", piINCBIN);
4100 urAddOp("MODULE", piMODULE);
4101 urAddOp("ENDMODULE", piENDMODULE);
4103 urAddOp("DUP", piDUP);
4104 urAddOp("EDUP", piEDUP);
4106 urAddOp("IF", piIF);
4107 urAddOp("IFX", piIFX);
4108 urAddOp("ELSE", piELSE);
4109 urAddOp("ELSEIF", piELSEIF);
4110 urAddOp("ELSEIFX", piELSEIFX);
4111 urAddOp("ENDIF", piENDIF);
4113 urAddOp("DEFINCR", piDEFINCR);
4114 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
4115 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
4116 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
4117 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
4118 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
4119 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
4120 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
4121 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
4123 urAddOp("$ERROR", piERROR);
4124 urAddOp("$WARNING", piWARNING);
4126 urAddOp("$PRINTF", piPRINTF);
4127 urAddOp("$PRINTF0", piPRINTF0);
4128 urAddOp("$PRINTFA", piPRINTFA);
4130 urAddOp("$MATHMODE", piMATHMODE);
4132 urAddOp("$FORTH_CONST", piForthConst);
4133 urAddOp("$FORTH_VAR", piForthVar);
4134 urAddOp("$FORTH_DVAR", piForthDVar);
4135 urAddOp("$FORTH_USER", piForthUser);
4137 urAddOp("$FORTH_CODE_WORD", piForthCodeWord);
4138 urAddOp("$FORTH_END_CODE_WORD", piForthCodeWordEnd);
4140 urAddOp("$FORTH_WORD", piForthWord);
4141 urAddOp("$FORTH_END_WORD", piForthWordEnd);
4143 urAddOp("$FORTH_DOES", piForthWordDoes);
4147 ///////////////////////////////////////////////////////////////////////////////
4148 // !0: invalid label
4150 static inline void fnSkipSpaces (const char *expr) {
4151 while (*expr && isSpace(*expr)) ++expr;
4152 return expr;
4157 static inline char fnNextChar (const char *expr) {
4158 while (*expr && isSpace(*expr)) ++expr;
4159 return *expr;
4163 #define FN_SKIP_BLANKS do { \
4164 while (*expr && isSpace(*expr)) ++expr; \
4165 } while (0)
4167 #define FN_CHECK_END do { \
4168 while (*expr && isSpace(*expr)) ++expr; \
4169 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
4170 ++expr; \
4171 } while (0)
4174 #define FN_CHECK_COMMA do { \
4175 while (*expr && isSpace(*expr)) ++expr; \
4176 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
4177 ++expr; \
4178 } while (0)
4181 static const char *readLabelName (char *buf, const char *expr) {
4182 int pos = 0;
4184 while (*expr && isSpace(*expr)) ++expr;
4185 for (;;) {
4186 char ch = *expr++;
4188 if (pos >= 128) return NULL;
4189 if (ch == ')') { --expr; break; }
4190 if (!ch) break;
4191 if (isAlphaDigit(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
4192 buf[pos++] = ch;
4193 } else {
4194 break;
4197 if (pos < 1) return NULL;
4198 buf[pos] = '\0';
4199 if (!urasm_is_valid_name(buf)) return NULL;
4200 return expr;
4204 static const char *fnDefKn (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
4205 char lbl[130];
4207 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
4208 FN_CHECK_END;
4209 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
4210 return expr;
4213 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); }
4214 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); }
4217 static const char *fnAligned256 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4218 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4219 FN_CHECK_END;
4220 if (!donteval) res->val = (res->val%256 ? 0 : 1);
4221 return expr;
4225 static const char *fnSameSeg (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4226 urasm_exprval_t v0, v1;
4228 urasm_exprval_init(&v0);
4229 urasm_exprval_init(&v1);
4230 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
4231 if (*error) return expr;
4232 FN_CHECK_COMMA;
4233 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
4234 if (*error) return expr;
4235 FN_CHECK_END;
4236 if (!donteval) res->val = (v0.val/256 == v1.val/256);
4237 return expr;
4241 static const char *fnAlign (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4242 urasm_exprval_t v0, v1;
4243 urasm_exprval_init(&v0);
4244 urasm_exprval_init(&v1);
4245 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
4246 if (*error) return expr;
4247 FN_SKIP_BLANKS;
4248 if (fnNextChar(expr) == ',') {
4249 FN_CHECK_COMMA;
4250 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
4251 if (*error) return expr;
4252 } else {
4253 v1.val = 256;
4255 FN_CHECK_END;
4256 if (!donteval) {
4257 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
4258 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
4260 return expr;
4264 static const char *fnLow (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4265 const char *ee = expr;
4266 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4267 FN_CHECK_END;
4268 if (!donteval) {
4269 if (res->fixuptype == UR_FIXUP_HIBYTE) {
4270 *error = UR_EXPRERR_FUNC; return ee;
4272 res->fixuptype = UR_FIXUP_LOBYTE;
4273 res->val &= 0xff;
4275 return expr;
4279 static const char *fnHigh (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4280 const char *ee = expr;
4281 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4282 FN_CHECK_END;
4283 if (!donteval) {
4284 if (res->fixuptype == UR_FIXUP_LOBYTE) {
4285 *error = UR_EXPRERR_FUNC; return ee;
4287 res->fixuptype = UR_FIXUP_HIBYTE;
4288 res->val = (res->val>>8)&0xff;
4290 return expr;
4294 static const char *fnAbs (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4295 const char *ee = expr;
4296 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4297 FN_CHECK_END;
4298 if (!donteval) {
4299 if (res->fixuptype != UR_FIXUP_NONE) {
4300 *error = UR_EXPRERR_FUNC; return ee;
4302 res->val = abs(res->val);
4304 return expr;
4308 static const char *fnBSwap (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4309 const char *ee = expr;
4310 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4311 FN_CHECK_END;
4312 if (!donteval) {
4313 if (res->fixuptype != UR_FIXUP_NONE) {
4314 *error = UR_EXPRERR_FUNC; return ee;
4316 res->val = ((res->val>>8)&0xff)|((res->val&0xff)<<8);
4318 return expr;
4322 static const char *fnScrAddr8xn (int nmul, urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4323 int32_t scrbase = 0x4000;
4324 int32_t x, y;
4325 const char *ee = expr;
4326 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
4327 // first argument is `x`
4328 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4329 x = res->val;
4330 // second argument is `y`
4331 FN_CHECK_COMMA;
4332 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4333 y = res->val*nmul;
4334 // optional third arg is screen base
4335 FN_SKIP_BLANKS;
4336 if (expr[0] == ',') {
4337 FN_CHECK_COMMA;
4338 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4339 scrbase = res->val&0xffff;
4341 FN_CHECK_END;
4342 if (!donteval) {
4343 //urasm_exprval_clear(res);
4344 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
4345 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
4346 if (y < 0 || y > 191) fatal("invalid y coordinate: %d", y/nmul);
4347 res->val = scrbase+
4348 (y/64)*2048+
4349 (y%8)*256+
4350 ((y%64)/8)*32+
4352 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
4354 return expr;
4358 static const char *fnScrAddr8x1 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4359 return fnScrAddr8xn(1, res, expr, addr, donteval, defined, error);
4362 static const char *fnScrAddr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4363 return fnScrAddr8xn(8, res, expr, addr, donteval, defined, error);
4367 static const char *fnScrAttr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4368 int32_t scrbase = 0x4000;
4369 int32_t x, y;
4370 const char *ee = expr;
4371 // first argument is `x`
4372 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4373 x = res->val;
4374 // second argument is `y`
4375 FN_CHECK_COMMA;
4376 urasm_exprval_clear(res);
4377 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4378 y = res->val;
4379 // optional third arg is screen base
4380 FN_SKIP_BLANKS;
4381 if (expr[0] == ',') {
4382 FN_CHECK_COMMA;
4383 urasm_exprval_clear(res);
4384 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
4385 scrbase = res->val&0xffff;
4387 urasm_exprval_clear(res);
4388 FN_CHECK_END;
4389 if (!donteval) {
4390 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
4391 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
4392 if (y < 0 || y > 23) fatal("invalid y coordinate: %d", y);
4393 res->val = scrbase+6144+y*32+x;
4395 return expr;
4399 static const char *fnMArgToStr (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4400 const char *ee = expr;
4401 // argument is macro argument name
4402 expr = strSkipSpacesConst(expr);
4403 if (expr[0] != '=') { *error = UR_EXPRERR_FUNC; return ee; }
4404 ++expr;
4405 if (!isAlpha(expr[0]) && expr[0] != '_') { *error = UR_EXPRERR_FUNC; return ee; }
4406 const char *nend = expr+1;
4407 while (isAlphaDigit(*nend) || *nend == '_') ++nend;
4408 if (nend-expr > 127) { *error = UR_EXPRERR_FUNC; return ee; }
4409 char name[128];
4410 memset(name, 0, sizeof(name));
4411 memcpy(name, expr, nend-expr);
4412 expr = nend;
4413 // parse index
4414 int index = 0;
4415 int has_index = 0;
4416 expr = strSkipSpacesConst(expr);
4417 if (expr[0] == '[') {
4418 expr = strSkipSpacesConst(expr+1);
4419 if (expr[0] != ']') {
4420 has_index = 1;
4421 int doneg = 0;
4422 if (expr[0] == '+') ++expr;
4423 else if (expr[0] == '-') { doneg = 1; ++expr; }
4424 index = 0;
4425 while (isDigit(expr[0])) {
4426 index = index*10+(expr[0]-'0');
4427 if (index >= 0x1fffffff) fatal("`margtostr` index too high");
4428 ++expr;
4430 expr = strSkipSpacesConst(expr);
4431 if (doneg) index = -index;
4433 if (expr[0] != ']') fatal("`margtostr` invalid index syntax");
4434 ++expr;
4436 // done
4437 FN_CHECK_END;
4438 if (curmacro == NULL) fatal("`margtostr` outside of macro");
4439 if (!donteval) {
4440 for (int f = 0; f < curmacro->mac->argc; ++f) {
4441 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
4442 // found argument, convert it to string
4443 if (!has_index) {
4444 res->str = realloc(res->str, strlen(curmacro->argvals[f])+1);
4445 strcpy(res->str, curmacro->argvals[f]);
4446 } else {
4447 const size_t slen = strlen(curmacro->argvals[f]);
4448 if (index < 0) {
4449 index = -index;
4450 if (index > slen) fatal("index out of string");
4451 index = (int)slen-index;
4453 if (index >= slen) fatal("index out of string");
4454 res->str = realloc(res->str, 2);
4455 res->str[0] = curmacro->argvals[f][index];
4456 res->str[1] = 0;
4458 // lowercase it
4459 for (char *s = res->str; *s; ++s) *s = toLower(*s);
4460 //fprintf(stderr, "<%s>\n", res->str);
4461 if (res->str[0]) {
4462 if (res->str[1]) {
4463 res->val = ((unsigned char)res->str[0]);
4464 res->val |= ((unsigned char)res->str[1])<<8;
4465 } else {
4466 res->val = (unsigned char)res->str[0];
4468 } else {
4469 res->val = 0;
4471 return expr;
4474 fatal("unknown macro argument '%s'", name);
4476 return expr;
4480 static const char *fnStrLen (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
4481 expr = strSkipSpacesConst(expr);
4482 int stlen = 0;
4483 switch (expr[0]) {
4484 case '"': case '\'': /* string literal? */
4486 char *a = (char *)expr+1;
4487 (void)parseStr(&a, expr[0], &stlen);
4488 expr = (const char *)a;
4490 break;
4491 default: /* expression */
4493 urasm_exprval_t r;
4494 urasm_exprval_init(&r);
4495 expr = urasm_expr_ex(&r, expr, disp, &donteval, defined, error);
4496 if (*error) fatalUrLib(*error);
4497 if (!r.str) fatal("string expected for `strlen()`");
4498 stlen = (int)strlen(r.str);
4499 urasm_exprval_clear(&r);
4501 break;
4503 FN_CHECK_END;
4504 if (!donteval) {
4505 urasm_exprval_setint(res, stlen);
4507 return expr;
4511 static void registerFunctions (void) {
4512 urasm_expr_register_func("defined", fnDefined);
4513 urasm_expr_register_func("known", fnKnown);
4514 urasm_expr_register_func("aligned256", fnAligned256);
4515 urasm_expr_register_func("align", fnAlign);
4516 urasm_expr_register_func("sameseg", fnSameSeg);
4517 urasm_expr_register_func("low", fnLow);
4518 urasm_expr_register_func("high", fnHigh);
4519 urasm_expr_register_func("abs", fnAbs);
4520 urasm_expr_register_func("bswap", fnBSwap);
4522 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8);
4523 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1);
4524 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8);
4526 urasm_expr_register_func("marg2str", fnMArgToStr);
4527 urasm_expr_register_func("strlen", fnStrLen);
4531 ///////////////////////////////////////////////////////////////////////////////
4532 // preparing another pass
4534 static void initPass (void) {
4535 curSrcLine = asmText;
4536 curModule = NULL;
4537 pc = start_pc;
4538 disp = start_disp;
4539 ent = start_ent;
4540 inTapeBlock = 0;
4541 tapeXorB = 0;
4542 wasOrg = 0;
4543 wasClr = 0;
4544 ifCount = 0;
4545 defIncr = 0;
4546 lblOptMakeU2 = 0;
4547 curmacronum = 0;
4548 lastSeenGlobalLabel = strdup(" [MAIN] ");
4549 modulesResetSeen();
4550 prepareMemory();
4551 urasm_use_old_priorities = 0;
4552 forthLatest = 0;
4553 forthCurrWord = NULL;
4557 static int posstPass (void) {
4558 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
4559 lastSeenGlobalLabel = NULL;
4560 if (checkLabels()) return -1;
4561 if (ifCount != 0) fatal("unbalanced IFs");
4562 if (forthCurrWord) fatal("unfinished forth word `%s`", forthCurrWord);
4563 return 0;
4567 ///////////////////////////////////////////////////////////////////////////////
4568 static int labelCmp (const void *aa, const void *bb) {
4569 if (aa == bb) return 0;
4570 const UrLabelInfo *a = *(const UrLabelInfo **)aa;
4571 const UrLabelInfo *b = *(const UrLabelInfo **)bb;
4572 return
4573 a->value < b->value ? -1 :
4574 a->value > b->value ? 1 :
4579 static void writeLabelsFile (const char *fname) {
4580 if (!fname || !fname[0]) return;
4581 // count labels
4582 int lcount = 0;
4583 for (const UrLabelInfo *ll = labels; ll; ll = ll->next) ++lcount;
4584 UrLabelInfo **larr = NULL;
4585 if (lcount > 0) {
4586 // create labels array
4587 larr = (UrLabelInfo **)malloc(sizeof(UrLabelInfo *)*lcount);
4588 lcount = 0;
4589 for (UrLabelInfo *ll = labels; ll; ll = ll->next, ++lcount) larr[lcount] = ll;
4590 // sort labels
4591 qsort(larr, lcount, sizeof(UrLabelInfo *), &labelCmp);
4593 // write labels
4594 FILE *fo = fopen(fname, "w");
4595 if (lcount > 0) {
4596 for (int f = 0; f < lcount; ++f) {
4597 UrLabelInfo *ll = larr[f];
4598 if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
4599 if (ll->value < 0) {
4600 fprintf(fo, "%d %s\n", ll->value, ll->name);
4601 } else {
4602 fprintf(fo, "#%04X %s\n", (unsigned)ll->value, ll->name);
4605 free(larr);
4607 // write forth words
4608 if (forthWordList) {
4609 fprintf(fo, "\n");
4610 for (ForthWord *w = forthWordList; w; w = w->next) {
4611 fprintf(fo, "#%04X %s\n", w->cfa, w->name);
4614 fclose(fo);
4618 ///////////////////////////////////////////////////////////////////////////////
4619 // options
4621 static struct option longOpts[] = {
4622 {"org", required_argument, NULL, 600},
4623 {"define", required_argument, NULL, 601},
4624 {"defzero", required_argument, NULL, 602},
4625 {"outdir", required_argument, NULL, 660},
4626 {"reffile", optional_argument, NULL, 669},
4628 {"sna", 0, NULL, 's'},
4629 {"sna128", 0, NULL, 'S'},
4630 {"tap", 0, NULL, 't'},
4631 {"autotap", 0, NULL, 'T'},
4632 {"raw", 0, NULL, 'r'},
4633 {"autodmb", 0, NULL, 'B'},
4634 {"dmb", 0, NULL, 'b'},
4635 {"none", 0, NULL, 'n'},
4636 {"help", 0, NULL, 'h'},
4637 {"hob", 0, NULL, 'H'},
4638 {"scl", 0, NULL, 'l'},
4639 {"autoscl", 0, NULL, 'L'},
4640 {"fixups", optional_argument, NULL, 'F'},
4642 {NULL, 0, NULL, 0}
4646 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
4649 static void usage (const char *pname) {
4650 printf(
4651 "usage: %s [options] infile\n"
4652 "default infiles:", pname);
4653 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
4654 printf("\n"
4655 "options:\n"
4656 " -s --sna write 48K .SNA file with autostart\n"
4657 " -S --sna128 write 148K .SNA file with autostart\n"
4658 " -t --tap write .tap file\n"
4659 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
4660 " -r --raw write raw file(s)\n"
4661 " -b --dmb write DMB file\n"
4662 " -B --autodmb write DMB file with autostart\n"
4663 " -H --hob write HoBeta code file(s)\n"
4664 " -l --scl write SCL TR-DOS archive\n"
4665 " -L --autoscl write autostarting SCL TR-DOS archive\n"
4666 " -n --none write nothing\n"
4667 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
4668 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
4669 " text: .txt file\n"
4670 " asm: address lists with counters\n"
4671 " asmdiff: 2nd and other addresses are relative, with counters\n"
4672 " asmz: address lists, with 0 end markers\n"
4673 " -h --help this help\n"
4674 "specials:\n"
4675 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
4676 " --org xxx set ORG\n"
4677 " --define val perform 'val EQU 1'\n"
4678 " --defzero val perform 'val EQU 0'\n"
4679 " --outdir dir output dir for resulting files (default: current)\n"
4684 ///////////////////////////////////////////////////////////////////////////////
4685 // main
4687 int main (int argc, char *argv[]) {
4688 int res = 0, c;
4689 const char *pname = argv[0];
4690 char *inFile = NULL;
4691 char **defines = NULL, **values = NULL;
4692 int defcount = 0;
4693 int wantref = 0;
4695 initInclideDir();
4697 urasm_getbyte = getByte;
4698 urasm_putbyte = putByte;
4699 urasm_label_by_name = findLabelCB;
4700 urasm_getval = getValueCB;
4701 urasm_expand = expandCB;
4702 urasm_fixup_operand = fixupOperandCB;
4704 //strcpy(tapeLoaderName, "cargador ");
4705 tapeLoaderName[0] = 0;
4707 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
4708 while ((c = getopt_long(argc, argv, "sStTrBbnhHFLl", longOpts, NULL)) >= 0) {
4709 switch (c) {
4710 case '?': return 1;
4711 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
4712 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
4713 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
4714 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
4715 case 'L': optRunSCL = 1; optWriteType = 'S'; optWTChanged = 1; break; /* with boot */
4716 case 'l': optRunSCL = -1; optWriteType = 'S'; optWTChanged = 1; break; /* no boot */
4717 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
4718 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
4719 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
4720 case 'h': usage(pname); res = 0; goto earlyerrquit;
4721 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
4722 case 'F':
4723 optWriteFixups = 1;
4724 if (optarg != NULL) {
4725 if (strcmp(optarg, "asm") == 0 || strcmp(optarg, "zas") == 0) {
4726 optFixupType = 1;
4727 } else if (strcmp(optarg, "asmdiff") == 0 || strcmp(optarg, "zasdiff") == 0) {
4728 optFixupType = 2;
4729 } else if (strcmp(optarg, "asmz") == 0 || strcmp(optarg, "zasz") == 0) {
4730 optFixupType = 3;
4731 } else if (strcmp(optarg, "text") == 0) {
4732 optFixupType = 0;
4733 } else {
4734 fprintf(stderr, "FATAL: invalid fixup type: '%s'\n", optarg);
4735 return 1;
4738 break;
4739 case 600: // org
4740 c = atoi(optarg);
4741 //fprintf(stderr, "ORG: %d\n", c);
4742 if (c < 0 || c > 65535) {
4743 fprintf(stderr, "FATAL: invalid ORG: %d\n", c);
4744 return 1;
4746 start_pc = start_disp = start_ent = c;
4747 break;
4748 case 601: // define
4749 //fprintf(stderr, "define: [%s]\n", optarg);
4750 defines = realloc(defines, sizeof(char *)*(defcount+1));
4751 values = realloc(values, sizeof(char *)*(defcount+1));
4752 defines[defcount] = strdup(optarg);
4753 values[defcount] = strdup("1");
4754 ++defcount;
4755 break;
4756 case 602: // defzero
4757 //fprintf(stderr, "defzero: [%s]\n", optarg);
4758 defines = realloc(defines, sizeof(char *)*(defcount+1));
4759 values = realloc(values, sizeof(char *)*(defcount+1));
4760 defines[defcount] = strdup(optarg);
4761 values[defcount] = strdup("0");
4762 ++defcount;
4763 break;
4764 case 660: // outdir
4765 if (optOutputDir != NULL) free(optOutputDir);
4766 optOutputDir = strdup(optarg);
4767 break;
4768 case 669: // reffile
4769 if (refFileName) free(refFileName);
4770 refFileName = (optarg ? strdup(optarg) : NULL);
4771 wantref = 1;
4772 break;
4776 if (optind >= argc) {
4777 // try to find default input file
4778 for (int f = 0; defInFiles[f]; ++f) {
4779 if (!access(defInFiles[f], R_OK)) {
4780 inFile = strdup(defInFiles[f]);
4781 break;
4784 } else {
4785 inFile = strdup(argv[optind]);
4788 if (!inFile || !inFile[0]) {
4789 res = 1;
4790 fprintf(stderr, "ERROR: no input file!\n");
4791 goto earlyerrquit;
4794 if (optOutputDir == NULL) optOutputDir = strdup(".");
4796 registerInstructions();
4797 registerFunctions();
4799 res = asmTextLoad(inFile, 0);
4800 if (!res) {
4801 for (int f = 0; f < defcount; ++f) {
4802 if (labelDoEQU(defines[f], values[f]) != 0) {
4803 fprintf(stderr, "FATAL: can't define label: '%s'\n", defines[f]);
4804 goto errquit;
4808 for (pass = 0; pass <= 1; ++pass) {
4809 initPass();
4810 printf("pass %d\n", pass);
4811 setCurSrcLine(asmText);
4812 if (setjmp(errJP)) { res = 1; break; }
4813 while (curSrcLine) processCurrentLine();
4814 if (posstPass()) { res = 1; break; }
4817 // write result
4818 if (res == 0) {
4819 char *oc = strdup(inFile);
4820 char *pd = strrchr(oc, '.');
4821 if (pd && !strchr(oc, '/')) *pd = '\0';
4822 switch (optWriteType) {
4823 case 's': saveSna(oc, optSNA48); break;
4824 case 't': saveTap(oc); break;
4825 case 'r': saveRaw(oc); break;
4826 case 'd': saveDMB(oc); break;
4827 case 'H': saveHob(oc); break;
4828 case 'S': saveSCL(oc); break;
4830 free(oc);
4831 if (optWriteFixups) writeFixups();
4832 if (wantref) {
4833 /* build ref file name */
4834 if (!refFileName || !refFileName[0]) {
4835 if (refFileName) free(refFileName);
4836 refFileName = malloc(strlen(inFile)+128);
4837 strcpy(refFileName, inFile);
4838 char *ext = strrchr(refFileName, '.');
4839 if (!ext) {
4840 strcat(refFileName, ".ref");
4841 } else {
4842 #ifdef WIN32
4843 char *slash = NULL;
4844 for (char *ts = refFileName; *ts; ++ts) {
4845 if (*ts == '/' || *ts == '\\') slash = ts;
4847 #else
4848 char *slash = strrchr(refFileName, '/');
4849 #endif
4850 if (!slash || slash < ext) {
4851 strcpy(ext, ".ref");
4852 } else {
4853 strcat(refFileName, ".ref");
4857 writeLabelsFile(refFileName);
4858 printf("refs written to '%s'\n", refFileName);
4859 free(refFileName);
4862 } else {
4863 fprintf(stderr, "ERROR: loading error!\n");
4866 errquit:
4867 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
4868 clearFixups();
4869 urClearLabels();
4870 modulesClear();
4871 asmTextClear();
4872 urClearOps();
4874 earlyerrquit:
4875 if (inFile) free(inFile);
4876 if (sysIncludeDir) free(sysIncludeDir);
4877 for (int f = defcount-1; f >= 0; --f) {
4878 free(values[f]);
4879 free(defines[f]);
4881 if (defines != NULL) { free(values); free(defines); }
4882 if (optOutputDir != NULL) free(optOutputDir);
4883 return (res ? 1 : 0);