liburasm: do not allow Z80N instructions in assembler if Z80A mode is active
[urasm.git] / src / urasm.c
blobf02794471f0d3c80e1195f402d26919e45e695bb
1 // URASM Z80 assembler
2 // coded by Ketmar // Invisible Vector
3 // GPLv3 or later
4 //
5 #include <getopt.h>
6 #include <setjmp.h>
7 #include <stdarg.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
19 #ifdef WIN32
20 # include <windows.h>
21 #endif
23 #include "liburasm/liburasm.h"
25 #include "ursna48.c"
28 #define VERSION_HI 0
29 #define VERSION_MID 2
30 #define VERSION_LO 2
33 #define MAYBE_UNUSED __attribute__((unused))
35 #define lambda(return_type, body_and_args) ({ \
36 return_type __fn__ body_and_args \
37 __fn__; \
41 ////////////////////////////////////////////////////////////////////////////////
42 static inline int isSpace (char ch) { return (ch && ((unsigned)(ch&0xff) <= 32 || ch == 127)); }
43 static inline int isAlpha (char ch) { return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')); }
44 static inline int isDigit (char ch) { return (ch >= '0' && ch <= '9'); }
45 static inline int isHexDigit (char ch) { return ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')); }
46 static inline int isAlphaDigit (char ch) { return (isAlpha(ch) || isDigit(ch)); }
48 static inline char toUpper (char ch) { return (ch >= 'a' && ch <= 'z' ? ch-'a'+'A' : ch); }
49 static inline char toLower (char ch) { return (ch >= 'A' && ch <= 'Z' ? ch-'A'+'a' : ch); }
52 ////////////////////////////////////////////////////////////////////////////////
53 static char *strprintfVA (const char *fmt, va_list vaorig) {
54 char *buf = NULL;
55 int olen, len = 128;
57 buf = malloc(len);
58 if (buf == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
59 for (;;) {
60 char *nb;
61 va_list va;
63 va_copy(va, vaorig);
64 olen = vsnprintf(buf, len, fmt, va);
65 va_end(va);
66 if (olen >= 0 && olen < len) return buf;
67 if (olen < 0) olen = len*2-1;
68 nb = realloc(buf, olen+1);
69 if (nb == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
70 buf = nb;
71 len = olen+1;
76 static __attribute((format(printf,1,2))) char *strprintf (const char *fmt, ...) {
77 char *buf = NULL;
78 va_list va;
80 va_start(va, fmt);
81 buf = strprintfVA(fmt, va);
82 va_end(va);
83 return buf;
87 ///////////////////////////////////////////////////////////////////////////////
88 // global variables
90 static char *sysIncludeDir = NULL;
91 static char *refFileName = NULL;
92 static char *lastIncludePath = NULL;
93 static char *lastSysIncludePath = NULL;
95 #define MAX_LINE_SIZE 16384
96 static char currLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
99 ///////////////////////////////////////////////////////////////////////////////
100 // init dirs
102 static void initInclideDir (void) {
103 const char *id = getenv("URASM_INCLUDE_DIR");
104 if (id && id[0]) {
105 sysIncludeDir = strdup(id);
106 } else {
107 char myDir[4096];
108 memset(myDir, 0, sizeof(myDir));
109 #ifndef WIN32
110 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
111 strcpy(myDir, ".");
112 } else {
113 char *p = (char *)strrchr(myDir, '/');
114 if (!p) strcpy(myDir, "."); else *p = '\0';
116 #else
117 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
118 char *p = strrchr(myDir, '\\');
119 if (!p) strcpy(myDir, "."); else *p = '\0';
120 #endif
121 strcat(myDir, "/libs");
122 sysIncludeDir = strdup(myDir);
124 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
125 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
129 ///////////////////////////////////////////////////////////////////////////////
130 // string utilities
132 /* trim trailing spaces and comments; normalize colons */
133 static void normalizeStr (char *s) {
134 char *p = s;
135 //int paren = 0;
136 // now skip all shit
137 while (*p) {
138 const char ch = *p++;
139 /* check for "af'" */
140 if (ch == '\'' && p-s >= 2 && toLower(p[-2] == 'a') && toLower(p[-1] == 'f')) continue;
141 /* parens */
142 //if (ch == '(') { ++paren; continue; }
143 //if (ch == ')') { if (--paren < 0) paren = 0; continue; }
144 /* comment */
145 if (ch == ';') { p[-1] = 0; break; }
146 /* string */
147 if (ch == '"' || ch == '\'') {
148 const char qch = ch;
149 while (*p) {
150 const char c1 = *p++;
151 if (c1 == qch) break;
152 if (c1 == '\\' && *p) ++p;
154 continue;
156 /* reduce and normalise colons */
157 if (/*paren == 0 &&*/ ch == ':') {
158 --p; /* back to colon */
159 /* remove spaces before colon */
160 char *t = p;
161 while (t != s && isSpace(t[-1])) --t;
162 if (t != p) memmove(t, p, strlen(p)+1);
163 p = t;
164 if (p[0] != ':') abort(); // assert
165 ++p; /* skip colon */
166 /* remove following spaces and colons */
167 t = p;
168 while (*t == ':' || isSpace(*t)) ++t;
169 if (t != p) memmove(p, t, strlen(t)+1);
170 continue;
173 /* done; trim trailing spaces and colons */
174 size_t slen = strlen(s);
175 while (slen > 0 && (isSpace(s[slen-1]) || s[slen-1] == ':')) --slen;
176 s[slen] = 0;
180 /* check if string starts with the given command (case-insensitive) */
181 /* returns NULL or pointer to args */
182 /* skips spaces after command if any */
183 static char *strIsCommand (const char *command, char *str) {
184 for (int cnt = 1; cnt > 0; --cnt) {
185 while (*str && isSpace(*str)) ++str; // skip spaces
186 for (; *command && *str; ++command, ++str) {
187 if (toUpper(*command) != toUpper(*str)) return NULL; // alas
189 if (*command) return NULL; // alas
190 if (*str && isAlphaDigit(*str)) return NULL; // alas
191 while (*str && isSpace(*str)) ++str; // skip spaces
192 if (*str && *str == ':') break; // try again if we have a colon
193 return str; // found
195 return NULL;
199 /* parse string literal */
200 /* don't free() result */
201 /* skips trailing spaces */
202 static char *parseStr (char **str, char endQ, int *lenp) {
203 static char buf[MAX_LINE_SIZE];
204 int len = 0, n, f, base;
205 char *a = *str;
207 int xDigit (char ch, int base) {
208 if (ch < '0') return -1;
209 if (base <= 10) {
210 if (ch >= '0'+base) return -1;
211 return ch-'0';
213 ch = toUpper(ch);
214 if (ch <= '9') return ch-'0';
215 if (ch < 'A' || ch > 'A'+base-10) return -1;
216 ch -= 'A'-10;
217 return (ch < base ? ch : -1);
220 memset(buf, 0, sizeof(buf));
221 if (lenp) *lenp = 0;
222 for (; *a; ++a) {
223 if (*a == '\\') {
224 if (!a[1]) break;
225 switch (*(++a)) {
226 case 'a': buf[len++] = '\a'; break;
227 case 'b': buf[len++] = '\b'; break;
228 case 'e': buf[len++] = '\x1b'; break;
229 case 'f': buf[len++] = '\f'; break;
230 case 'n': buf[len++] = '\n'; break;
231 case 'r': buf[len++] = '\r'; break;
232 case 't': buf[len++] = '\t'; break;
233 case 'v': buf[len++] = '\v'; break;
234 case 'z': buf[len++] = '\0'; break;
235 case 'x': case 'X': // hex
236 ++a; // skip 'x'
237 base = 16; f = 2;
238 donum: for (n = 0; f > 0; --f) {
239 char ch = xDigit(*a++, base);
241 if (ch < 0) { --a; break; }
242 n *= base;
243 n += ch;
245 buf[len++] = n;
246 --a; // return to the last digit, 'for' will skip it
247 break;
248 case '0': // octal
249 base = 8; f = 4;
250 goto donum;
251 case '1' ... '9': // decimal
252 base = 10; f = 3;
253 goto donum;
254 default: buf[len++] = a[0]; break; // others
256 } else {
257 if (*a == endQ) { ++a; break; }
258 buf[len++] = *a;
261 while (*a && isSpace(*a)) ++a; // skip trailing spaces
262 *str = a;
263 buf[len] = '\0';
264 if (lenp) *lenp = len;
265 return buf;
269 ///////////////////////////////////////////////////////////////////////////////
270 // source file stack, reader, etc
272 typedef struct SourceLine {
273 struct SourceLine *next;
274 char *line;
275 char *fname;
276 int lineNo;
277 int system;
278 } SourceLine;
280 static SourceLine *asmText = NULL;
281 static SourceLine *asmTextLast = NULL;
282 static SourceLine *curSrcLine = NULL;
284 #define MAX_MACRO_ARGS (32)
286 typedef struct MacroDef {
287 struct MacroDef *next;
288 char *name;
289 SourceLine *lines;
290 int argc;
291 char *argdefaults[MAX_MACRO_ARGS]; // default values
292 char *argnames[MAX_MACRO_ARGS]; // argument names
293 } MacroDef;
295 typedef struct {
296 MacroDef *mac;
297 char *argvals[MAX_MACRO_ARGS]; // argument values
298 } CurMacroDef;
300 static MacroDef *maclist = NULL;
301 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
302 static int curmacronum = 0;
305 static MacroDef *findMacro (const char *name) {
306 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strcasecmp(name, mc->name) == 0) return mc;
307 return NULL;
311 static void asmTextClear (void) {
312 while (asmText) {
313 SourceLine *l = asmText;
315 asmText = asmText->next;
316 free(l->line);
317 free(l->fname);
318 free(l);
320 asmTextLast = curSrcLine = NULL;
324 static int asmTextLoad (const char *fname, int system) {
325 FILE *fl;
326 int lineNo = 0;
327 SourceLine *s;
329 if (!(fl = fopen(fname, "r"))) {
330 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
331 return -1;
333 printf("loading: %s\n", fname);
334 // read file
335 while (fgets(currLine, sizeof(currLine)-1, fl)) {
336 ++lineNo;
337 currLine[sizeof(currLine)-1] = '\0';
338 normalizeStr(currLine);
339 //fprintf(stderr, "*[%s]\n", curLine);
340 if (!currLine[0]) continue; // don't store empty lines
341 // add current line
342 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
343 s->lineNo = lineNo;
344 s->system = system;
345 if ((s->line = strdup(currLine)) == NULL) abort();
346 if ((s->fname = strdup(fname)) == NULL) abort();
347 if (asmTextLast) asmTextLast->next = s; else asmText = s;
348 asmTextLast = s;
350 fclose(fl);
351 return 0;
355 static char *extractFileDir (const char *s) {
356 if (!s || !s[0]) return strdup("");
357 const char *slash;
358 #ifdef WIN32
359 slash = NULL;
360 for (const char *ts = s; *ts; ++ts) {
361 if (*ts == '/' || *ts == '\\') slash = ts;
363 if (slash == s && (s[0] == '/' || s[0] == '\\') && (s[1] == '/' || s[1] == '\\')) slash = NULL;
364 #else
365 slash = strrchr(s, '/');
366 #endif
367 if (!slash) return strdup("");
368 ptrdiff_t len = (ptrdiff_t)(slash-s)+1;
369 char *res = malloc(len+1);
370 memcpy(res, s, len);
371 res[len] = 0;
372 #ifdef WIN32
373 while (len > 0 && (res[len-1] == '\\' || res[len-1] == '/')) --len;
374 #else
375 while (len > 0 && res[len-1] == '/') --len;
376 #endif
377 if (len == 0) { free(res); return strdup(""); }
378 res[len] = 0;
379 return res;
383 static void loadCurSrcLine (void) {
384 if (curSrcLine) {
385 strcpy(currLine, curSrcLine->line);
386 /* macros will not change include dirs */
387 if (!curmacro) {
388 char **incpp = (!curSrcLine->system ? &lastIncludePath : &lastSysIncludePath);
389 if (*incpp) free(*incpp);
390 *incpp = extractFileDir(curSrcLine->fname);
392 } else {
393 currLine[0] = 0;
397 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
399 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
402 static inline int strHasPathDelim (const char *s) {
403 if (!s || !s[0]) return 0;
404 #ifdef WIN32
405 return (strchr(s, '/') || strchr(s, '\\') ? 1 : 0);
406 #else
407 return (strchr(s, '/') ? 1 : 0);
408 #endif
412 /* returns malloced string */
413 static char *createIncludeName (const char *fname, int assystem, const char *defaultmain) {
414 if (!fname || !fname[0]) return NULL;
415 char *res;
416 if (fname[0] != '/') {
417 const char *incdir;
418 if (!assystem) {
419 incdir = lastIncludePath;
420 } else {
421 incdir = lastSysIncludePath;
422 if (!incdir || !incdir[0]) incdir = sysIncludeDir;
424 res = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
425 } else {
426 res = strprintf("%s", fname);
428 struct stat st;
429 if (defaultmain && defaultmain[0]) {
430 if (stat(res, &st) == 0) {
431 if (S_ISDIR(st.st_mode)) {
432 char *rs = strprintf("%s/%s", res, defaultmain);
433 free(res);
434 res = rs;
438 /* check if there is disk file */
439 if (strHasPathDelim(fname) && stat(res, &st) != 0) {
440 /* no file, try "root include" */
441 const char *incdir = (!assystem ? NULL : sysIncludeDir);
442 char *rs = strprintf("%s/%s", (incdir && incdir[0] ? incdir : "."), fname);
443 free(res);
444 res = rs;
445 /* check for dir again */
446 if (defaultmain && defaultmain[0]) {
447 if (stat(res, &st) == 0) {
448 if (S_ISDIR(st.st_mode)) {
449 char *rs = strprintf("%s/%s", res, defaultmain);
450 free(res);
451 res = rs;
456 //fprintf(stderr, "inc: fname=<%s>; sys=%d; def=<%s>; res=<%s>\n", fname, assystem, defaultmain, res);
457 return res;
461 static int includeCount = 0;
463 // process 'INCLUDE'
464 // include file instead of the current line
465 static int asmTextInclude (const char *fname, int system) {
466 char *fn;
467 FILE *fl;
468 int lineNo = 0;
469 SourceLine *first = NULL, *last = NULL, *s = NULL;
471 if (includeCount > 256) {
472 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine->fname, curSrcLine->lineNo);
473 return -1;
476 fn = createIncludeName(fname, system, "zzmain.zas");
477 ++includeCount;
478 printf("loading: %s\n", fn);
479 if ((fl = fopen(fn, "r")) == NULL) {
480 fprintf(stderr, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine->fname, curSrcLine->lineNo, currLine);
481 free(fn);
482 return -1;
485 while (fgets(currLine, sizeof(currLine)-1, fl)) {
486 ++lineNo;
487 currLine[sizeof(currLine)-1] = '\0';
488 const size_t slen = strlen(currLine);
489 if (slen == 0 || (currLine[slen-1] != '\n' && currLine[slen-1] != '\r')) {
490 fprintf(stderr, "ERROR: file %s, line %d: line too long\n", fn, lineNo);
491 free(fn);
492 return -1;
494 normalizeStr(currLine);
495 if (!currLine[0]) continue; // don't store empty lines
496 // add current line
497 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
498 s->system = system;
499 s->lineNo = lineNo;
500 if ((s->line = strdup(currLine)) == NULL) abort();
501 if ((s->fname = strdup(fn)) == NULL) abort();
502 if (last != NULL) last->next = s; else first = s;
503 last = s;
505 fclose(fl);
506 free(fn);
507 --includeCount;
508 curSrcLine->line[0] = 0;
509 if (last) {
510 last->next = curSrcLine->next;
511 curSrcLine->next = first;
513 return 0;
517 ///////////////////////////////////////////////////////////////////////////////
518 // prototypes
520 static void processCurrentLine (void); // only one, will skip to next one
523 ///////////////////////////////////////////////////////////////////////////////
524 // error raisers, etc
526 static jmp_buf errJP;
529 static void errorWriteFile (FILE *fo) {
530 if (curSrcLine) {
531 fprintf(fo, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
532 } else {
533 fprintf(fo, "somewhere in time: ");
537 static void errorMsgV (const char *fmt, va_list ap) {
538 errorWriteFile(stderr);
539 vfprintf(stderr, fmt, ap);
540 va_end(ap);
541 fputc('\n', stderr);
542 fflush(stderr);
546 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
547 va_list ap;
548 fprintf(stderr, "WARNING ");
549 va_start(ap, fmt);
550 errorMsgV(fmt, ap);
554 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
555 va_list ap;
556 fprintf(stderr, "FATAL ");
557 va_start(ap, fmt);
558 errorMsgV(fmt, ap);
562 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
563 va_list ap;
564 va_start(ap, fmt);
565 errorMsgV(fmt, ap);
566 longjmp(errJP, 666);
570 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
571 errorMsg("%s", urasm_errormsg(errcode));
572 longjmp(errJP, 666);
576 //////////////////////////////////////////////////////////////////////////////
577 // operator management
579 // return !0 to skip current line
580 typedef int (*UrAsmOpFn) (void);
582 enum {
583 PI_CONT_LINE = 0,
584 PI_SKIP_LINE = 1
587 typedef struct UrAsmOp {
588 char *name;
589 UrAsmOpFn fn;
590 struct UrAsmOp *next;
591 } UrAsmOp;
593 static UrAsmOp *oplist = NULL;
596 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
597 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
598 if (!res) abort();
599 res->name = strdup(name);
600 res->fn = fn;
601 res->next = oplist;
602 oplist = res;
603 return res;
607 static UrAsmOp *urFindOp (const char *name) {
608 UrAsmOp *res;
609 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
610 return res;
614 static void urClearOps (void) {
615 while (oplist) {
616 UrAsmOp *c = oplist;
618 oplist = oplist->next;
619 free(c->name);
620 free(c);
625 ///////////////////////////////////////////////////////////////////////////////
626 // label management
628 typedef struct UrLabelInfo {
629 char *name;
630 int32_t value;
631 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
632 int known; /* !0: label value already known */
633 int refLine; /* first referenced line */
634 int fixuptype; /* UR_FIXUP_XXX */
635 char *refFile;
636 struct UrLabelInfo *next;
637 } UrLabelInfo;
639 static UrLabelInfo *labels = NULL;
642 static void urClearLabels (void) {
643 UrLabelInfo *c;
645 while ((c = labels) != NULL) {
646 labels = c->next;
647 if (c->name) free(c->name);
648 if (c->refFile) free(c->refFile);
649 free(c);
654 static UrLabelInfo *urFindLabel (const char *name) {
655 for (UrLabelInfo *c = labels; c; c = c->next) if (strcmp(name, c->name) == 0) return c;
656 return NULL;
660 static UrLabelInfo *urAddLabel (const char *name) {
661 UrLabelInfo *c = urFindLabel(name);
663 if (c == NULL) {
664 UrLabelInfo *p;
666 for (p = NULL, c = labels; c; p = c, c = c->next) {}
667 c = calloc(1, sizeof(UrLabelInfo));
668 if (!c) abort();
669 c->name = strdup(name);
670 c->type = -1;
671 c->fixuptype = UR_FIXUP_NONE;
672 if (p) p->next = c; else labels = c;
673 c->next = NULL;
675 return c;
679 ///////////////////////////////////////////////////////////////////////////////
680 // module list management
682 typedef struct ModuleInfo {
683 char *name;
684 char *fname; // opened in this file
685 int seen; // !0: module already seen, skip other definitions from the same file
686 struct ModuleInfo *next;
687 } ModuleInfo;
689 static ModuleInfo *modules = NULL;
690 static ModuleInfo *curModule = NULL;
693 static void modulesClear (void) {
694 curModule = NULL;
695 while (modules) {
696 ModuleInfo *c = modules;
698 modules = modules->next;
699 free(c->name);
700 free(c->fname);
701 free(c);
706 static void modulesResetSeen (void) {
707 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
711 static ModuleInfo *moduleFind (const char *name) {
712 if (!name || !name[0]) return NULL;
713 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
714 return NULL;
718 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
719 ModuleInfo *c;
721 if (!name || !fname || !name[0] || !fname[0]) abort();
722 if ((c = calloc(1, sizeof(ModuleInfo))) == NULL) abort();
723 if ((c->name = strdup(name)) == NULL) abort();
724 if ((c->fname = strdup(fname)) == NULL) abort();
725 c->next = modules;
726 return (modules = c);
730 ///////////////////////////////////////////////////////////////////////////////
731 // fixup management
732 typedef struct FixupItem {
733 struct FixupItem *next;
734 uint16_t opdestaddr;
735 uint16_t opaddr;
736 int fixuptype;
737 int size;
738 } FixupItem;
739 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
742 static void clearFixups (void) {
743 FixupItem *c;
745 while ((c = fixlisthead) != NULL) {
746 fixlisthead = c->next;
747 free(c);
752 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
753 FixupItem *fx = calloc(1, sizeof(FixupItem));
755 fx->opdestaddr = opdestaddr;
756 fx->opaddr = opaddr;
757 fx->fixuptype = fixuptype;
758 fx->size = size;
760 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
761 fx->next = NULL;
762 fixlisttail = fx;
766 ///////////////////////////////////////////////////////////////////////////////
767 // destination memory management
769 static uint8_t memory[65536];
770 static char memused[65536];
771 static char memresv[65536];
772 static uint16_t start_pc = 0x100; // viva CP/M!
773 static uint16_t start_disp = 0x100; // viva CP/M!
774 static uint16_t start_ent = 0x100; // viva CP/M!
775 static uint16_t pc = 0; /* current position to write */
776 static uint16_t disp = 0; /* current 'virtual PC' */
777 static uint16_t ent = 0; /* starting address */
778 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
779 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
780 static int inTapeBlock = 0;
781 static uint8_t tapeXorB = 0;
784 static inline uint8_t getByte (uint16_t addr) {
785 return memory[addr];
790 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
791 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
796 static inline void putByte (uint16_t addr, uint8_t b) {
797 if (inTapeBlock) tapeXorB ^= b;
798 memory[addr] = b;
799 memused[addr] = 1;
803 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
804 putByte(addr, w&0xFFU);
805 putByte(addr+1, (w>>8)&0xFFU);
809 static inline void emitByte (uint8_t b) {
810 putByte(pc, b);
811 ++pc;
812 ++disp;
816 static inline void emitWord (uint16_t w) {
817 emitByte(w&0xFFU);
818 emitByte((w>>8)&0xFFU);
822 static inline void emitRWord (uint16_t w) {
823 emitByte((w>>8)&0xFFU);
824 emitByte(w&0xFFU);
828 static void prepareMemory (void) {
829 memset(memory, 0, sizeof(memory));
830 memset(memused, 0, sizeof(memused));
831 memset(memresv, 0, sizeof(memresv));
835 ///////////////////////////////////////////////////////////////////////////////
836 // label getter and utilities
838 static char *lastSeenGlobalLabel = NULL; /* global */
841 static char *fixLocalLabel (const char *name) {
842 static char newname[MAX_LINE_SIZE*2+1024];
844 memset(newname, 0, sizeof(newname));
845 if (!name || !name[0]) {
846 newname[0] = '\0';
847 } else if (!lastSeenGlobalLabel || name[0] != '.') {
848 strcpy(newname, name);
849 } else {
850 if (name[0] == '.' && name[1] == '.') {
851 // this is macro label
852 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
853 sprintf(newname, "{#MAC%d:%s:%s}", curmacronum, curmacro->mac->name, name);
854 } else {
855 // this is local label, let's rename it
856 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
858 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
860 return newname;
864 static char *fixGlobalLabel (const char *name) {
865 static char newname[MAX_LINE_SIZE*2+1024];
867 memset(newname, 0, sizeof(newname));
868 if (!name || !name[0]) {
869 newname[0] = '\0';
870 } else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
871 if (name[0] == '@' && name[1]) ++name;
872 strcpy(newname, name);
873 } else {
874 // this is global unqualified label and we have a module; let's rename it
875 sprintf(newname, "%s.%s", curModule->name, name);
877 //printf("%s --> %s\n", name, newname);
878 return newname;
882 static int lblOptMakeU2 = 0;
884 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
885 UrLabelInfo *lbl;
886 char *ln, *nn;
888 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
889 lbl = urFindLabel(nn);
890 if (!lbl) {
891 // try non-module label
892 lbl = urFindLabel(ln);
894 if (!lbl) {
895 if (pass != 0) {
896 errorMsg("using undefined label %s", ln);
897 *found = 0;
898 *defined = 0;
899 return 0;
901 lbl = urAddLabel(nn);
902 lbl->type = (lblOptMakeU2 ? -42 : -1);
903 lbl->known = 0;
904 lbl->refLine = curSrcLine->lineNo;
905 lbl->refFile = strdup(curSrcLine->fname);
906 //printf("new label: [%s]\n", lbl->name);
907 } else {
908 //printf("label reference: [%s]\n", lbl->name);
910 if (lbl) {
911 *found = 1;
912 *defined = lbl->known!=0;
913 *fixuptype = lbl->fixuptype;
914 return lbl->value;
916 *found = 0;
917 *defined = 0;
918 return 0;
922 // qtypes
923 enum {
924 UR_QTYPE_DEFINED,
925 UR_QTYPE_KNOWN
928 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
929 UrLabelInfo *lbl;
930 char *ln, *nn;
932 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
933 lbl = urFindLabel(nn);
934 if (!lbl) {
935 // try non-module label
936 lbl = urFindLabel(ln);
938 switch (qtype) {
939 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
940 case UR_QTYPE_KNOWN: return lbl!=NULL;
941 default: ;
943 return 0;
947 static void fixupOperandCB (const urasm_operand_t *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
948 if (pass == 1) {
949 //static const char *n[4] = {"none", "word", "low", "high"};
950 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
951 addFixup(opdestaddr, opaddr, fixuptype, size);
956 static int checkLabels (void) {
957 int wasError = 0;
959 for (UrLabelInfo *c = labels; c; c = c->next) {
960 if (c->type == -1) {
961 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
962 wasError = 1;
964 if (c->type == 0) c->known = -1;
966 //if (wasError) longjmp(errJP, 667);
967 return wasError;
971 ///////////////////////////////////////////////////////////////////////////////
972 // expression utils (aka string parsing)
975 /* skip leading spaces */
976 /* returns string with spaces skipped */
977 static inline char *strSkipSpaces (const char *s) {
978 while (*s && isSpace(*s)) ++s;
979 return (char *)s;
983 /* skip leading spaces */
984 /* returns string with spaces skipped */
985 static inline const char *strSkipSpacesConst (const char *s) {
986 while (*s && isSpace(*s)) ++s;
987 return s;
991 /* remove trailing spaces from string */
992 static void strTrimRight (char *s) {
993 if (!s || !s[0]) return;
994 size_t len = strlen(s);
995 while (len > 0 && isSpace(s[len-1])) --len;
996 s[len] = 0;
1000 /* skip leading spaces and colons */
1001 /* returns string with spaces skipped */
1002 static inline char *strSkipSpacesColons (char *s) {
1003 while (*s && (isSpace(*s) || *s == ':')) ++s;
1004 return s;
1008 /* remove leading spaces from the current line */
1009 static inline void removeSpaces (void) {
1010 char *e = strSkipSpaces(currLine);
1011 if (e != currLine) memmove(currLine, e, strlen(e)+1);
1015 /* skip leading spaces, and argument (up to, but not including comma or colon) */
1016 /* correctly skip strings */
1017 /* returns string after skipped argument (with trailing spaces skipped) */
1018 static char *skipMacroArg (char *str) {
1019 int parens = 0;
1020 char *strstart = str;
1021 for (;;) {
1022 str = strSkipSpaces(str);
1023 if (!str[0]) return str;
1024 if (parens == 0 && (str[0] == ',' || str[0] == ':')) return str;
1025 /* check for "af'" */
1026 if (str[0] == '\'' && str-strstart >= 2 && toLower(str[-2] == 'a') && toLower(str[-1] == 'f')) {
1027 ++str;
1028 continue;
1030 if (str[0] == '(') { ++parens; continue; }
1031 if (str[0] == ')') { if (--parens < 0) parens = 0; continue; }
1032 /* check for string */
1033 if (str[0] == '"' || str[0] == '\'') {
1034 const char qch = *str++;
1035 while (*str) {
1036 const char ch = *str++;
1037 if (ch == qch) break;
1038 if (ch == '\\' && *str) ++str;
1040 continue;
1042 ++str;
1047 /* evaluate next numeric expression in input string */
1048 /* returns expression value */
1049 static int32_t getExprArg (int *defined, int *addr) {
1050 int error = 0;
1051 char *a = strSkipSpaces(currLine);
1052 int32_t res;
1053 if (!a[0]) fatal("expression expected");
1054 const char *ee = urasm_expr(&res, a, disp, defined, addr, &error);
1055 if (error) fatalUrLib(error);
1056 if (*ee) {
1057 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
1058 memmove(currLine, ee, strlen(ee)+1);
1059 } else {
1060 currLine[0] = '\0';
1062 return res;
1066 /* evaluate next string expression in input string */
1067 /* returns expression value */
1068 static char *getStrExprArg (void) {
1069 int error = 0;
1070 int donteval = 0, defined = 0;
1071 static char resbuf[256];
1072 char *a = strSkipSpaces(currLine);
1073 if (!a[0]) fatal("expression expected");
1074 urasm_exprval_t res;
1075 urasm_exprval_init(&res);
1076 const char *ee = urasm_expr_ex(&res, a, disp, &donteval, &defined, &error);
1077 if (error) fatalUrLib(error);
1078 if (*ee) {
1079 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
1080 memmove(currLine, ee, strlen(ee)+1);
1081 } else {
1082 currLine[0] = '\0';
1084 if (res.str) {
1085 snprintf(resbuf, sizeof(resbuf), "%s", res.str);
1086 } else {
1087 snprintf(resbuf, sizeof(resbuf), "%d", res.val);
1089 urasm_exprval_clear(&res);
1090 return resbuf;
1094 /* evaluate next numeric expression in input string */
1095 /* there shoild be no other expressions in the string */
1096 /* returns expression value */
1097 static int32_t getOneExprArg (int *defined, int *addr) {
1098 int32_t res = getExprArg(defined, addr);
1099 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
1100 return res;
1104 /* is next expression a string literal? */
1105 static inline int isStrArg (void) {
1106 const char *s = strSkipSpaces(currLine);
1107 return (s[0] == '"' || s[0] == '\'');
1111 /* check of we reached end of operator */
1112 static __attribute__((unused)) inline int isOperatorEnd (void) {
1113 const char *s = strSkipSpaces(currLine);
1114 return (s[0] == 0 || s[0] == ':');
1118 /* check of we reached end of operator */
1119 static __attribute__((unused)) inline int isLineEnd (void) {
1120 const char *s = strSkipSpaces(currLine);
1121 return (s[0] == 0);
1125 /* parse string argument from input string */
1126 /* returns parsed string */
1127 static char *getStrArg (int *lenp) {
1128 char *res, qCh;
1129 char *a = strSkipSpaces(currLine);
1130 qCh = *a++;
1131 if (qCh != '"' && qCh != '\'') fatal("string expected");
1132 res = parseStr(&a, qCh, lenp);
1133 if (*a) {
1134 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
1135 memmove(currLine, a, strlen(a)+1);
1136 } else {
1137 currLine[0] = '\0';
1139 return res;
1143 /* get identifier (and lowercase it) */
1144 static char *getOneIdArgLo (void) {
1145 static char res[MAX_LINE_SIZE+128];
1146 char *p;
1147 char *a = strSkipSpaces(currLine);
1148 memset(res, 0, sizeof(res));
1149 if (!a[0]) fatal("identifier expected");
1150 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1151 for (; p > res && isSpace(p[-1]); --p) {}
1152 *p = '\0';
1153 if (p-res > 120) fatal("identifier too long: %s", res);
1154 while (*a && isSpace(*a)) ++a;
1155 if (*a) {
1156 memmove(currLine, a, strlen(a)+1);
1157 if (currLine[0] == ';') currLine[0] = 0;
1158 if (currLine[0]) fatal("extra arguments");
1159 } else {
1160 currLine[0] = '\0';
1162 for (char *t = res; *t; ++t) *t = toLower(*t);
1163 return res;
1167 /* get label argument */
1168 static char *getLabelArg (int checkdelim) {
1169 static char res[MAX_LINE_SIZE+128];
1170 char *p;
1171 char *a = strSkipSpaces(currLine);
1172 memset(res, 0, sizeof(res));
1173 if (!a[0]) fatal("label expected");
1174 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1175 for (; p > res && isSpace(p[-1]); --p) {}
1176 *p = '\0';
1177 if (p-res > 120) fatal("label name too long: %s", res);
1178 while (*a && isSpace(*a)) ++a;
1179 if (*a) {
1180 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
1181 memmove(currLine, a, strlen(a)+1);
1182 } else {
1183 currLine[0] = '\0';
1185 return res;
1189 /* get label argument, and ensure that it is the last one */
1190 static char *getOneLabelArg (void) {
1191 char *res = getLabelArg(1);
1192 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
1193 return res;
1197 /* returns ',' or 0 */
1198 static char eatComma (void) {
1199 char *a = strSkipSpaces(currLine);
1200 if (!a[0]) { currLine[0] = '\0'; return 0; }
1201 if (a[0] == ':') return 0;
1202 if (a[0] != ',') fatal("invalid expression: ',' expected");
1203 for (++a; *a && isSpace(*a); ++a) {}
1204 if (!a[0]) { currLine[0] = '\0'; return 0; }
1205 memmove(currLine, a, strlen(a)+1);
1206 return ',';
1210 ///////////////////////////////////////////////////////////////////////////////
1211 // label processor
1213 static MAYBE_UNUSED void removeSpacesAndColons (void) {
1214 char *ep = strSkipSpacesColons(currLine);
1215 memmove(currLine, ep, strlen(ep)+1);
1219 static void checkExprEnd (void) {
1220 char *ep = strSkipSpaces(currLine);
1221 memmove(currLine, ep, strlen(ep)+1);
1222 if (currLine[0] && currLine[0] != ':') fatal("end of expression expected");
1226 static void checkOperatorEnd (void) {
1227 char *ep = strSkipSpaces(currLine);
1228 memmove(currLine, ep, strlen(ep)+1);
1229 if (currLine[0]) fatal("end of operator expected");
1233 /* remove label from curLine */
1234 static void removeLabel (void) {
1235 char *ep = currLine;
1236 if (ep[0] && !isSpace(ep[0])) for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {} // skip text
1237 // skip spaces and colons
1238 ep = strSkipSpacesColons(ep);
1239 memmove(currLine, ep, strlen(ep)+1);
1243 static int labelDoEQU (const char *lblname, const char *value) {
1244 static char n2[256];
1245 UrLabelInfo *lbl;
1247 if (value == NULL || lblname == NULL || !lblname[0] || strlen(lblname) > 255 || strlen(value) >= MAX_LINE_SIZE) return -1;
1248 memset(n2, 0, sizeof(n2));
1249 strcpy(n2, lblname);
1250 if (!urasm_is_valid_name(n2)) return -1; // this is not a valid label, get out of here
1251 // check if this can be an instruction
1252 lbl = urAddLabel(lblname);
1253 if (!lbl->refFile) {
1254 lbl->refLine = 0;
1255 lbl->refFile = strdup("artificially-defined-label");
1258 strcpy(currLine, value);
1261 int defined = 1, addr = UR_FIXUP_NONE;
1262 int32_t res = getOneExprArg(&defined, &addr);
1264 lbl->type = 1; // equ label
1265 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1266 if (defined) {
1267 lbl->value = res;
1268 lbl->known = 1;
1269 } else {
1270 return -1; //fatal("can't calculate label %s", lbl->name);
1273 return 0;
1277 static void processLabel (void) {
1278 char *argstart;
1279 char *ep, *ln, *nn;
1280 static char n2[256];
1281 UrLabelInfo *lbl;
1282 int noLocAff = 0, doEQU = 0;
1283 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1284 memset(n2, 0, sizeof(n2));
1285 if (!currLine[0] || isSpace(currLine[0]) || currLine[0] == ':') {
1286 // this may be " id = smth" or " id equ smth"
1287 ep = currLine;
1288 // skip spaces
1289 while (isSpace(*ep)) ++ep;
1290 if (ep[0] == ':' || !ep[0] || (!isAlpha(ep[0]) && ep[0] != '_' && ep[0] != '.' && ep[0] != '@')) {
1291 removeLabel(); // removeLabel() removes any spaces, etc
1292 return;
1294 // this looks like a label; check for '=' or 'equ'
1295 while (isAlphaDigit(ep[0]) || ep[0] == '_' || ep[0] == '.' || ep[0] == '@') ++ep;
1296 nn = ep;
1297 // skip trailing spaces
1298 while (isSpace(*ep)) ++ep;
1299 if (ep[0] == '=') {
1300 doEQU = 0;
1301 argstart = ++ep;
1302 } else if (isSpace(*nn)) {
1303 doEQU = 1;
1304 argstart = strIsCommand("EQU", ep);
1305 } else {
1306 argstart = NULL;
1308 if (!argstart) {
1309 removeLabel(); // removeLabel() removes any spaces, etc
1310 return;
1312 // remove leading spaces from name
1313 // copy label
1314 ep = currLine;
1315 // skip spaces
1316 while (isSpace(*ep)) ++ep;
1317 if (ep >= nn) fatal("internal compiler error");
1318 if (nn-ep > 120) fatal("label too long");
1319 memset(n2, 0, sizeof(n2));
1320 memmove(n2, ep, nn-ep);
1321 if (urFindOp(n2) || !urasm_is_valid_name(n2)) {
1322 //fatal("invalid label name");
1323 removeLabel(); // removeLabel() removes any spaces, etc
1324 return;
1326 // remove label name
1327 memmove(currLine, argstart, strlen(argstart)+1);
1328 // append label
1329 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1330 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1331 lbl = urAddLabel(nn);
1332 if (!lbl->refFile) {
1333 lbl->refLine = curSrcLine->lineNo;
1334 lbl->refFile = strdup(curSrcLine->fname);
1336 if (doEQU && pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1337 int defined = 1, addr = UR_FIXUP_NONE;
1338 int32_t res = getOneExprArg(&defined, &addr);
1339 lbl->type = doEQU;
1340 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1341 if (defined) {
1342 lbl->value = res;
1343 lbl->known = 1;
1344 } else {
1345 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1347 currLine[0] = '\0';
1348 return;
1350 // collect label
1351 for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {}
1352 if (ep-currLine > 120) fatal("label too long");
1353 // copy label
1354 memset(n2, 0, sizeof(n2));
1355 memmove(n2, currLine, ep-currLine);
1356 if (urFindOp(n2)) {
1357 ep = strSkipSpaces(ep);
1358 if (*ep != ':') return; // this must be an instruction, process it
1360 if (!urasm_is_valid_name(n2)) return; // this is not a valid label, get out of here
1361 // check for macro
1362 if (findMacro(n2)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
1363 // check if this can be instruction
1364 //ep = strSkipSpaces(ep);
1365 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1366 // ok, we got a good label
1367 removeLabel();
1368 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1369 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1370 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1371 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1372 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1373 lbl = urAddLabel(nn);
1374 if (!lbl->refFile) {
1375 lbl->refLine = curSrcLine->lineNo;
1376 lbl->refFile = strdup(curSrcLine->fname);
1378 //printf("new: [%s]\n", lbl->name);
1379 // get command name
1380 if (currLine[0] == '=') {
1381 doEQU = 0;
1382 argstart = currLine+1;
1383 } else {
1384 doEQU = 1;
1385 argstart = strIsCommand("EQU", currLine);
1387 if (!argstart || doEQU) {
1388 if (pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1390 if (argstart) {
1391 // do '=' or 'EQU'
1392 memmove(currLine, argstart, strlen(argstart)+1);
1393 if (!doEQU) {
1394 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label '%s'", lbl->name);
1396 int defined = 1, addr = UR_FIXUP_NONE;
1397 int32_t res = getOneExprArg(&defined, &addr);
1398 lbl->type = doEQU;
1399 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1400 if (defined) {
1401 lbl->value = res;
1402 lbl->known = 1;
1403 } else {
1404 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1406 currLine[0] = '\0';
1407 return;
1409 // code label
1410 if (lbl->name[0] != '{' && !noLocAff) {
1411 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
1412 lastSeenGlobalLabel = strdup(lbl->name);
1414 lbl->type = 2;
1415 lbl->value = disp;
1416 lbl->known = 1;
1417 lbl->fixuptype = UR_FIXUP_WORD;
1421 ///////////////////////////////////////////////////////////////////////////////
1422 // instruction finder (in source)
1424 /* array ends with NULL */
1425 /* returns line or NULL */
1426 /* iidx will be set to found instruction number */
1427 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
1428 if (iidx) *iidx = -1;
1429 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
1430 va_list ap;
1431 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1432 va_start(ap, iidx);
1433 for (int f = 0; ;++f) {
1434 const char *name = va_arg(ap, const char *);
1436 if (!name) break;
1437 if (strIsCommand(name, cur->line)) {
1438 va_end(ap);
1439 if (iidx) *iidx = f;
1440 return cur;
1443 va_end(ap);
1445 return NULL;
1449 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
1450 return findNextInstructionFromList(NULL, name, NULL);
1454 ///////////////////////////////////////////////////////////////////////////////
1455 // writers
1457 static int optWriteFixups = 0;
1458 static int optFixupType = 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1459 static int optRunTape = 1;
1460 static int optRunDMB = 1;
1461 static int optRunSCL = 1;
1462 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1463 static int optSNA48 = 1;
1464 static char *optOutputDir = NULL;
1467 ///////////////////////////////////////////////////////////////////////////////
1468 static void writeFixups (void) {
1470 void writeFixupList (FILE *fo, int cnt, const char *lbl, int (*chk)(const FixupItem *)) {
1471 if (cnt > 0) {
1472 int prevaddr = 0;
1473 fprintf(fo, "%s:\n", lbl);
1474 if (optFixupType == 1) fprintf(fo, " dw %d ; count\n", cnt);
1475 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1476 if (chk(fx)) {
1477 fprintf(fo, " dw #%04X\n", fx->opaddr-prevaddr);
1478 if (optFixupType == 2) {
1479 prevaddr = fx->opaddr;
1483 if (optFixupType == 2 || optFixupType == 3) fprintf(fo, " dw 0 ; end marker\n");
1487 if (optFixupType == 0) {
1488 /* simple text file */
1489 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.txt");
1490 printf("writing fixups to '%s'...\n", fname);
1491 FILE *fo = fopen(fname, "w");
1492 free(fname);
1493 if (fo == NULL) fatal("can't write fixup file");
1494 fprintf(fo, "; addr dadr sz\n");
1495 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1496 static const char type[4] = "NWLH";
1497 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
1499 fclose(fo);
1500 } else {
1501 /* various asm formats */
1502 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.zas");
1503 printf("writing fixups to '%s'...\n", fname);
1504 FILE *fo = fopen(fname, "w");
1505 int cntw = 0, cntwl = 0, cntwh = 0, cntbl = 0, cntbh = 0;
1506 free(fname);
1507 if (fo == NULL) fatal("can't write fixup file");
1508 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1509 if (fx->fixuptype == UR_FIXUP_WORD) { ++cntw; continue; }
1510 switch (fx->fixuptype) {
1511 case UR_FIXUP_LOBYTE: if (fx->size == 2) ++cntwl; else ++cntbl; break;
1512 case UR_FIXUP_HIBYTE: if (fx->size == 2) ++cntwh; else ++cntbh; break;
1515 writeFixupList(fo, cntw, "fixup_table_w", lambda(int, (const FixupItem *fx) {
1516 return (fx->fixuptype == UR_FIXUP_WORD);
1517 }));
1518 writeFixupList(fo, cntwl, "fixup_table_wl", lambda(int, (const FixupItem *fx) {
1519 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 2);
1520 }));
1521 writeFixupList(fo, cntwh, "fixup_table_wh", lambda(int, (const FixupItem *fx) {
1522 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 2);
1523 }));
1524 writeFixupList(fo, cntbl, "fixup_table_bl", lambda(int, (const FixupItem *fx) {
1525 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 1);
1526 }));
1527 writeFixupList(fo, cntbh, "fixup_table_bh", lambda(int, (const FixupItem *fx) {
1528 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 1);
1529 }));
1530 fclose(fo);
1535 ///////////////////////////////////////////////////////////////////////////////
1536 /* return 'found' flag */
1537 static int findChunkFrom (int addr, int *start, int *len) {
1538 if (addr < 0) addr = 0;
1539 for (; addr <= 65535 && !memused[addr]; ++addr) {}
1540 if (addr > 65535) return 0;
1541 *start = addr;
1542 for (; addr <= 65535 && memused[addr]; ++addr) {}
1543 *len = addr-(*start);
1544 return 1;
1548 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
1549 for (; buflen >= 2; buflen -= 2) {
1550 int cnt = *buf++;
1551 uint8_t byte = *buf++;
1553 if (cnt == 0) {
1554 // copy
1555 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
1556 if (!memused[addr]) putByte(addr, *buf);
1557 if (addr == 0xffff) return;
1559 } else {
1560 // fill
1561 for (; cnt > 0; --cnt, ++addr) {
1562 if (!memused[addr]) putByte(addr, byte);
1563 if (addr == 0xffff) return;
1570 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1571 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1572 if (bxor) *bxor = (*bxor)^b;
1573 return 0;
1577 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1578 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1579 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1580 return 0;
1584 ///////////////////////////////////////////////////////////////////////////////
1585 // .sna
1587 static int saveSna (const char *fname, int as48) {
1588 char *fn;// = malloc(strlen(fname)+16);
1589 uint8_t regs[27];
1590 FILE *fo;
1591 char abuf[32];
1593 fn = strprintf("%s/%s.sna", optOutputDir, fname);
1594 fo = fopen(fn, "wb");
1595 free(fn);
1596 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1597 printf("out: %s.sna\n", fname);
1598 memmove(regs, ursna48, 27);
1599 #if 0
1600 if (optRunSNA) {
1601 /* push new address */
1602 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1603 sp -= 2;
1604 putByte(sp, ent&0xFFU);
1605 putByte(sp+1, (ent>>8)&0xFFU);
1606 regs[23] = sp&0xFFU;
1607 regs[24] = (sp>>8)&0xFFU;
1609 fwrite(regs, 27, 1, fo);
1610 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1611 fwrite(memory+16384, 49152, 1, fo);
1612 #endif
1614 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1616 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1617 //fprintf(stderr, "%d\n", memused[sp]);
1618 if (memused[sp] || memused[(sp+1)&0xffff]) {
1619 fprintf(stderr, "FATAL: can't save snapshot!\n");
1620 goto error;
1623 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
1624 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
1625 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1627 sprintf(abuf, "%05d", clrAddr);
1628 memcpy(memory+23762, abuf, 5);
1629 sprintf(abuf, "%05d", ent);
1630 memcpy(memory+23773, abuf, 5);
1632 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
1634 if (!as48) {
1635 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
1637 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
1638 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
1639 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
1640 // write pages
1641 memset(memory, 0, 16384);
1642 for (int f = 1; f < 8; ++f) {
1643 if (f != 2 && f != 5) {
1644 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
1649 fclose(fo);
1650 return 0;
1651 error:
1652 fclose(fo);
1653 unlink(fname);
1654 return -1;
1658 ///////////////////////////////////////////////////////////////////////////////
1659 // bin chunks
1661 static void saveRaw (const char *basename) {
1662 char *fname = NULL;// = malloc(strlen(basename)+16);
1663 int start = 0, len;
1664 FILE *fo;
1666 while (findChunkFrom(start, &start, &len)) {
1667 if (fname != NULL) free(fname);
1668 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, (optTapExt ? "tap" : "bin"));
1669 fo = fopen(fname, "wb");
1670 if (!fo) {
1671 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1672 } else {
1673 printf("out: %s\n", fname);
1674 if (fwrite(memory+start, len, 1, fo) != 1) {
1675 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1676 fclose(fo);
1677 unlink(fname);
1678 } else {
1679 fclose(fo);
1682 start += len;
1684 if (fname != NULL) free(fname);
1688 ///////////////////////////////////////////////////////////////////////////////
1689 // HoBeta files
1691 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
1692 char name[8];
1693 char type;
1694 uint16_t start;
1695 uint16_t len;
1696 uint8_t zero; // always zero
1697 uint8_t secLen; // length in sectors
1698 uint16_t checksum;
1699 } HOBHeader;
1702 static uint16_t calcHobSum (const void *hdr) {
1703 const uint8_t *buf = (const uint8_t *)hdr;
1704 uint16_t res = 0;
1706 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
1707 return res;
1711 static void saveHob (const char *basename) {
1712 char *fname = NULL;//malloc(strlen(basename)+16);
1713 int start = 0, len;
1714 HOBHeader hdr;
1715 FILE *fo;
1717 while (findChunkFrom(start, &start, &len)) {
1718 if (fname != NULL) free(fname);
1719 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, "$C");
1720 fo = fopen(fname, "wb");
1721 if (!fo) {
1722 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1723 start += len;
1724 } else {
1725 char tmpbuf[sizeof(hdr.name)*2];
1726 printf("out: %s\n", fname);
1727 memset(&hdr, 0, sizeof(hdr));
1728 memset(&hdr.name, 32, 8);
1729 snprintf(tmpbuf, sizeof(tmpbuf), "%c%04X", basename[0], start);
1730 while (strlen(tmpbuf) < sizeof(hdr.name)) strcat(tmpbuf, " "); /* sorry! */
1731 memcpy(hdr.name, tmpbuf, sizeof(hdr.name));
1732 hdr.type = 'C';
1733 hdr.start = start;
1734 hdr.len = len;
1735 hdr.secLen = (len+255)/256;
1736 hdr.checksum = calcHobSum(&hdr);
1737 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
1738 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1739 fclose(fo);
1740 unlink(fname);
1741 goto quit;
1743 if (fwrite(memory+start, len, 1, fo) != 1) {
1744 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1745 fclose(fo);
1746 unlink(fname);
1747 goto quit;
1749 start += len;
1750 while (len%256) {
1751 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
1752 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1753 fclose(fo);
1754 unlink(fname);
1755 goto quit;
1757 ++len;
1758 //fprintf(stderr, ":%d\n", len%256);
1760 fclose(fo);
1763 quit:
1764 if (fname != NULL) free(fname);
1768 // ////////////////////////////////////////////////////////////////////////// //
1769 typedef struct {
1770 uint8_t fcount;
1771 uint8_t dir[14*256];
1772 uint32_t dirpos;
1773 uint32_t fpos;
1774 uint8_t *data;
1775 size_t datapos;
1776 size_t datasize;
1777 } SCLFile;
1780 static void sclInit (SCLFile *scl) {
1781 memset(scl, 0, sizeof(*scl));
1782 scl->datasize = 2*80*16*256; /* maximum disk size */
1783 scl->data = malloc(scl->datasize);
1784 memset(scl->data, 0, scl->datasize);
1785 scl->fpos = 0xffffffffu;
1789 static void sclFree (SCLFile *scl) {
1790 if (!scl) return;
1791 if (scl->data) free(scl->data);
1792 memset(scl, 0, sizeof(*scl));
1793 scl->fpos = 0xffffffffu;
1797 static void sclStartFile (SCLFile *scl, const char *name, char type, uint16_t v0, uint16_t v1) {
1798 if (scl->fcount == 255) {
1799 fprintf(stderr, "FATAL: too many files in SCL!\n");
1800 exit(1);
1802 if (scl->fpos != 0xffffffffu) {
1803 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1804 exit(1);
1806 int nlen = 0;
1807 while (nlen < 8 && name && name[nlen]) scl->dir[scl->dirpos++] = name[nlen++];
1808 while (nlen < 8) { scl->dir[scl->dirpos++] = ' '; ++nlen; }
1809 scl->dir[scl->dirpos++] = type;
1810 scl->dir[scl->dirpos++] = v0&0xff;
1811 scl->dir[scl->dirpos++] = (v0>>8)&0xff;
1812 scl->dir[scl->dirpos++] = v1&0xff;
1813 scl->dir[scl->dirpos++] = (v1>>8)&0xff;
1814 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1815 scl->fpos = scl->datapos;
1819 static void sclEndFile (SCLFile *scl) {
1820 if (scl->fpos == 0xffffffffu) {
1821 fprintf(stderr, "FATAL: last SCL file already finished!\n");
1822 exit(1);
1824 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
1825 const uint32_t fsz = scl->datapos-scl->fpos;
1826 if (fsz > 255*256) {
1827 fprintf(stderr, "FATAL: SCL file too big!\n");
1828 exit(1);
1830 scl->dir[scl->dirpos++] = ((fsz+255)/256)&0xff; /* size in sectors */
1831 ++scl->fcount;
1832 scl->fpos = 0xffffffffu;
1836 static void sclWriteData (SCLFile *scl, const void *buf, size_t len) {
1837 if (scl->fpos == 0xffffffffu) {
1838 fprintf(stderr, "FATAL: no open SCL file!\n");
1839 exit(1);
1841 if (!len) return;
1842 if (len > 255*256) {
1843 fprintf(stderr, "FATAL: SCL file too big!\n");
1844 exit(1);
1846 memcpy(scl->data+scl->datapos, buf, len);
1847 scl->datapos += len;
1851 static void sclWriteByte (SCLFile *scl, uint8_t b) {
1852 sclWriteData(scl, &b, 1);
1856 static void sclWriteWord (SCLFile *scl, uint16_t w) {
1857 uint8_t b = w&0xff;
1858 sclWriteData(scl, &b, 1);
1859 b = (w>>8)&0xff;
1860 sclWriteData(scl, &b, 1);
1864 #define SCL_DO_WRITE(buf_,size_) do { \
1865 if ((size_) == 0) break; \
1866 const uint8_t *p = (const uint8_t *)(buf_); \
1867 for (size_t n = (size_t)(size_); n--; ++p) checksum += *p; \
1868 if (fwrite((buf_), (size_t)(size_), 1, fo) != 1) return -1; \
1869 } while (0)
1871 // <0: error; 0: ok
1872 static int sclSaveToFile (FILE *fo, SCLFile *scl) {
1873 if (scl->fpos != 0xffffffffu) {
1874 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1875 exit(1);
1877 const char *sign = "SINCLAIR";
1878 uint32_t checksum = 0;
1879 // header
1880 SCL_DO_WRITE(sign, 8);
1881 SCL_DO_WRITE(&scl->fcount, 1);
1882 // directory
1883 SCL_DO_WRITE(scl->dir, scl->dirpos);
1884 // data
1885 SCL_DO_WRITE(scl->data, scl->datapos);
1886 // write checksum
1887 for (unsigned f = 0; f < 4; ++f) {
1888 const uint8_t b = (checksum>>(f*8))&0xff;
1889 SCL_DO_WRITE(&b, 1);
1891 // done
1892 return 0;
1896 // ////////////////////////////////////////////////////////////////////////// //
1897 static void saveSCLCargador (SCLFile *scl) {
1898 static uint8_t cargador[16384]; // should be enough for everyone
1899 int linestart = -1;
1900 int linenum = 0;
1901 int start = 0, len, pos;
1903 void putStr (const char *s) {
1904 for (; *s; ++s) cargador[pos++] = *s;
1907 void putNum (int num) {
1908 char buf[64];
1909 sprintf(buf, "%d", num);
1910 putStr(buf);
1913 void startLine () {
1914 ++linenum;
1915 linestart = pos;
1916 // number
1917 cargador[pos++] = (linenum>>8)&0xff;
1918 cargador[pos++] = linenum&0xff;
1919 // size (will be fixed later)
1920 cargador[pos++] = 0;
1921 cargador[pos++] = 0;
1924 void flushLine () {
1925 if (linestart >= 0) {
1926 const int size = pos-linestart-4;
1927 cargador[linestart+2] = size&0xff;
1928 cargador[linestart+3] = (size>>8)&0xff;
1930 linestart = -1;
1933 char cname[16];
1935 pos = 0;
1936 startLine();
1937 // CLEAR VAL "xxx"
1938 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
1939 // load blocks
1940 int cont = 1;
1941 while (findChunkFrom(start, &start, &len)) {
1942 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1943 if (cont) { putStr(":"); cont = 0; }
1944 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1945 // generate chunk name
1946 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
1947 putStr(cname);
1948 //" CODE
1949 putStr("\"\xaf\r");
1950 flushLine();
1951 startLine();
1952 start += len;
1954 // and run
1955 if (cont) { putStr(":"); cont = 0; }
1956 // RANDOMIZE USR VAL "xxx"
1957 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"");
1958 putStr("\r");
1959 flushLine();
1961 //putWord(0xaa80);
1962 //putWord(1); // autostart line
1964 // write to SCL
1965 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
1966 sclWriteData(scl, cargador, (size_t)pos);
1967 sclWriteByte(scl, 0x80);
1968 sclWriteByte(scl, 0xaa);
1969 sclWriteWord(scl, 1);
1970 sclEndFile(scl);
1974 static void saveSCL (const char *basename) {
1975 SCLFile scl;
1976 int fcount = 0;
1977 int start, len;
1978 char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
1979 // count files
1980 start = 0;
1981 while (findChunkFrom(start, &start, &len)) {
1982 ++fcount;
1983 start += len;
1985 if (fcount && optRunSCL) fcount += 2; // +loader and boot
1986 if (fcount > 255) {
1987 fprintf(stderr, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname, fcount);
1988 exit(1);
1990 // create output file
1991 FILE *fo = fopen(fname, "wb");
1992 if (!fo) {
1993 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
1994 exit(1);
1996 // initialise SCL writer
1997 sclInit(&scl);
1998 // write loader
1999 if (fcount && optRunSCL) {
2000 // create simple boot
2001 const uint8_t dasboot[] = {
2002 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"
2004 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
2005 sclWriteWord(&scl, 1); // line number
2006 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
2007 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
2008 // TR-DOS signature
2009 sclWriteByte(&scl, 0x80);
2010 sclWriteByte(&scl, 0xaa);
2011 // start line
2012 sclWriteWord(&scl, 0);
2013 sclEndFile(&scl);
2014 saveSCLCargador(&scl);
2016 // write chunks
2017 char cname[16];
2018 start = 0;
2019 while (findChunkFrom(start, &start, &len)) {
2020 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
2021 sclStartFile(&scl, cname, 'C', (unsigned)start, (unsigned)len);
2022 sclWriteData(&scl, memory+(unsigned)start, (unsigned)len);
2023 sclEndFile(&scl);
2024 start += len;
2026 if (sclSaveToFile(fo, &scl) < 0) {
2027 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
2028 fclose(fo);
2029 unlink(fname);
2030 exit(1);
2032 sclFree(&scl);
2033 fclose(fo);
2034 printf("out: %s\n", fname);
2035 if (fname != NULL) free(fname);
2039 ///////////////////////////////////////////////////////////////////////////////
2040 // .dmb
2042 static int saveDMB (const char *fname) {
2043 char *fn;// = malloc(strlen(fname)+16);
2044 int start = 0, len;
2045 uint16_t pcnt = 0;
2046 FILE *fo;
2048 fn = strprintf("%s/%s.dmb", optOutputDir, fname);
2049 fo = fopen(fn, "wb");
2050 free(fn);
2051 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
2053 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
2055 printf("out: %s.dmb\n", fname);
2056 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
2057 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
2058 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
2059 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
2061 start = 0;
2062 while (findChunkFrom(start, &start, &len)) {
2063 if (fWriteWord(fo, len, NULL) != 0) goto error;
2064 if (fWriteWord(fo, start, NULL) != 0) goto error;
2065 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
2066 start += len;
2068 fclose(fo);
2069 return 0;
2070 error:
2071 fclose(fo);
2072 unlink(fname);
2073 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
2074 return -1;
2078 ///////////////////////////////////////////////////////////////////////////////
2079 // .tap
2081 static char tapeLoaderName[16];
2084 static void saveTapCargador (FILE *fo) {
2085 // count blocks
2086 static uint8_t cargador[16384]; // should be enough for everyone
2087 int start = 0, len, pos, f;
2088 uint8_t bxor;
2091 void putStr (const char *s) {
2092 for (; *s; ++s) cargador[pos++] = *s;
2095 void putNum (int num) {
2096 char buf[64];
2097 sprintf(buf, "%d", num);
2098 putStr(buf);
2102 pos = 4;
2103 // number
2104 cargador[0] = 0; cargador[1] = 10;
2105 // size (will be fixed later)
2106 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
2107 // load blocks
2108 while (findChunkFrom(start, &start, &len)) {
2109 // :LOAD "" CODE
2110 putStr(":\xef\"\"\xaf");
2111 start += len;
2113 // and run
2114 // :RANDOMIZE USR VAL "xxx"
2115 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
2116 // patch len
2117 cargador[2] = (pos-4)&0xff;
2118 cargador[3] = ((pos-4)>>8)&0xff;
2119 // write header
2120 fWriteWord(fo, 19, NULL); // length of header
2121 bxor = 0;
2122 fWriteByte(fo, 0, &bxor); // header block
2123 fWriteByte(fo, 0, &bxor); // 'basic' flag
2124 if (tapeLoaderName[0]) {
2125 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2126 } else {
2127 fWriteByte(fo, 'c', &bxor);
2128 fWriteByte(fo, 'a', &bxor);
2129 fWriteByte(fo, 'r', &bxor);
2130 fWriteByte(fo, 'g', &bxor);
2131 fWriteByte(fo, 'a', &bxor);
2132 fWriteByte(fo, 'd', &bxor);
2133 fWriteByte(fo, 'o', &bxor);
2134 fWriteByte(fo, 'r', &bxor);
2135 fWriteByte(fo, ' ', &bxor);
2136 fWriteByte(fo, ' ', &bxor);
2138 fWriteWord(fo, pos, &bxor); // length
2139 fWriteWord(fo, 10, &bxor); // start
2140 fWriteWord(fo, pos, &bxor); // length2
2141 fWriteByte(fo, bxor, NULL);
2142 // write data
2143 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
2144 bxor = 0;
2145 fWriteByte(fo, 0xFFU, &bxor); // data block
2146 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
2147 fWriteByte(fo, bxor, NULL);
2151 static void saveTap (const char *basename) {
2152 char *fname;// = malloc(strlen(basename)+16);
2153 char blkname[128];
2154 int start = 0, len, f;
2155 uint8_t bxor;
2156 FILE *fo;
2158 fname = strprintf("%s/%s.tap", optOutputDir, basename);
2159 fo = fopen(fname, "wb");
2160 free(fname);
2161 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
2162 printf("out: %s.tap\n", basename);
2163 if (optRunTape) saveTapCargador(fo);
2164 while (findChunkFrom(start, &start, &len)) {
2165 // write header
2166 if (tapeLoaderName[0]) {
2167 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2168 memcpy(blkname, tapeLoaderName, 10);
2169 } else {
2170 sprintf(blkname, "c%04X:%04X", start, len);
2172 //printf(" block: %s\n", blkname);
2173 fWriteWord(fo, 19, NULL); // length of header
2174 bxor = 0;
2175 fWriteByte(fo, 0, &bxor); // header block
2176 fWriteByte(fo, 3, &bxor); // 'code' flag
2177 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
2178 fWriteWord(fo, len, &bxor);
2179 fWriteWord(fo, start, &bxor);
2180 fWriteWord(fo, 32768, &bxor);
2181 fWriteByte(fo, bxor, NULL);
2182 // write data
2183 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
2184 bxor = 0;
2185 fWriteByte(fo, 0xFFU, &bxor); // data block
2186 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
2187 fWriteByte(fo, bxor, NULL);
2188 start += len;
2190 fclose(fo);
2194 ///////////////////////////////////////////////////////////////////////////////
2195 // pseudoinstructions
2197 // note that processCurrentLine() will NOT skip to the next line before
2198 // calling pseudoinstruction handler!
2199 // note that processCurrentLine() will skip current line after calling
2200 // pseudoinstruction handler!
2202 static int wasOrg = 0;
2203 static int wasClr = 0;
2206 // ////////////////////////////////////////////////////////////////////////// //
2207 // print message using printf-like syntax
2208 // doesn't print new line
2209 static void processPrintf (FILE *fo, const char *fmt) {
2210 if (!fmt || !fmt[0]) return;
2211 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2212 char *tempstr = NULL;
2213 size_t tempsize = 0;
2214 char *fmtcopy = malloc(strlen(fmt)+1);
2215 strcpy(fmtcopy, fmt);
2216 char *currfmt = fmtcopy;
2217 char tempbuf[512];
2218 int stlen;
2219 char *strarg;
2220 int defined;
2221 int32_t exprval;
2222 while (*currfmt) {
2223 /* find '%' */
2224 char *prcs = strchr(currfmt, '%');
2225 if (!prcs || !prcs[1]) {
2226 /* no more formatting; print the tail and exit the loop */
2227 fprintf(fo, "%s", currfmt);
2228 break;
2230 /* is this "%%"? */
2231 int docheck = 1;
2232 if (prcs[1] == '%') {
2233 ++prcs;
2234 docheck = 0;
2236 /* print up to `prcs` */
2237 if (prcs > currfmt) {
2238 size_t partlen = (ptrdiff_t)(prcs-currfmt);
2239 if (partlen+1 > tempsize) {
2240 tempsize = ((partlen+8)|0xff)+1;
2241 tempstr = realloc(tempstr, tempsize);
2243 memcpy(tempstr, currfmt, partlen);
2244 tempstr[partlen] = 0;
2245 fprintf(fo, "%s", tempstr);
2247 currfmt = ++prcs; /* skip percent */
2248 if (!docheck) continue;
2249 /* parse format */
2250 char sign = ' ';
2251 int width = 0;
2252 int zerofill = 0;
2253 char ftype = ' ';
2254 if (*currfmt == '+' || *currfmt == '-') sign = *currfmt++;
2255 if (sign != '-' && *currfmt == '0') zerofill = 1;
2256 while (isDigit(*currfmt)) { width = width*10+((*currfmt)-'0'); ++currfmt; }
2257 if (width > 256) width = 256;
2258 ftype = *currfmt++;
2259 if (!ftype) break; /* oops */
2260 if (!eatComma()) fatal("out of arguments for string format");
2261 switch (ftype) {
2262 case 's': /* string */
2263 stlen = 0;
2264 switch (strSkipSpaces(currLine)[0]) {
2265 case '"': case '\'': /* string literal? */
2266 strarg = getStrArg(&stlen);
2267 break;
2268 default: /* expression */
2269 strarg = getStrExprArg();
2270 stlen = (int)strlen(strarg);
2271 break;
2273 /* left pad */
2274 if (sign != '-' && stlen < width) {
2275 int padlen = width-stlen;
2276 memset(tempbuf, ' ', padlen);
2277 tempbuf[padlen+1] = 0;
2278 fprintf(fo, "%s", tempbuf);
2280 fprintf(fo, "%s", strarg);
2281 /* right pad */
2282 if (sign == '-' && stlen < width) {
2283 int padlen = width-stlen;
2284 memset(tempbuf, ' ', padlen);
2285 tempbuf[padlen+1] = 0;
2286 fprintf(fo, "%s", tempbuf);
2288 break;
2289 case 'd': /* decimal */
2290 defined = 1;
2291 exprval = getExprArg(&defined, NULL);
2292 if (width && zerofill) {
2293 fprintf(fo, "%0*d", width, exprval);
2294 } else if (width) {
2295 fprintf(fo, "%*d", (sign != '-' ? width : -width), exprval);
2296 } else {
2297 fprintf(fo, "%d", exprval);
2299 break;
2300 case 'c': /* char */
2301 defined = 1;
2302 exprval = getExprArg(&defined, NULL);
2303 if (exprval <= 0 || exprval == '?' || exprval > 255) exprval = '?';
2304 if (width) {
2305 fprintf(fo, "%*c", (sign != '-' ? width : -width), exprval);
2306 } else {
2307 fprintf(fo, "%c", exprval);
2309 break;
2310 case 'u': /* unsigned */
2311 defined = 1;
2312 exprval = getExprArg(&defined, NULL);
2313 if (width && zerofill) {
2314 fprintf(fo, "%0*u", width, (unsigned)(exprval&0xffff));
2315 } else if (width) {
2316 fprintf(fo, "%*u", (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2317 } else {
2318 fprintf(fo, "%u", (unsigned)(exprval&0xffff));
2320 break;
2321 case 'x': case 'X': /* hex */
2322 defined = 1;
2323 exprval = getExprArg(&defined, NULL);
2324 if (width && zerofill) {
2325 fprintf(fo, (ftype == 'x' ? "%0*x" : "%0*X"), width, (unsigned)(exprval&0xffff));
2326 } else if (width) {
2327 fprintf(fo, (ftype == 'x' ? "%*x" : "%*X"), (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2328 } else {
2329 fprintf(fo, (ftype == 'x' ? "%x" : "%X"), (unsigned)(exprval&0xffff));
2331 break;
2332 default:
2333 if (ftype <= 0 || ftype == 127) ftype = '?';
2334 fatal("invalid format specifier: '%c'", ftype);
2335 break;
2338 if (tempstr) free(tempstr);
2339 free(fmtcopy);
2343 ///////////////////////////////////////////////////////////////////////////////
2344 // user error
2346 static int piERROR (void) {
2347 int len = 0;
2348 char *res = getStrArg(&len);
2349 fprintf(stdout, "*** USER ERROR: ");
2350 processPrintf(stdout, res);
2351 fputc('\n', stdout);
2352 fatal("user error abort");
2353 return PI_SKIP_LINE;
2357 static int piWARNING (void) {
2358 int len = 0;
2359 char *res = getStrArg(&len);
2360 fprintf(stdout, "*** USER WARNING ");
2361 if (curSrcLine) {
2362 fprintf(stdout, "at file %s, line %d: ", curSrcLine->fname, curSrcLine->lineNo);
2363 } else {
2364 fprintf(stdout, "somewhere in time: ");
2366 processPrintf(stdout, res);
2367 fputc('\n', stdout);
2368 return PI_SKIP_LINE;
2372 ///////////////////////////////////////////////////////////////////////////////
2373 // user warnings
2375 static int piPrintfCommon (int passNo) {
2376 if (passNo < 0 || pass == passNo) {
2377 int len = 0;
2378 char *res = getStrArg(&len);
2379 processPrintf(stdout, res);
2380 fputc('\n', stdout);
2382 return PI_SKIP_LINE;
2386 static int piDISPLAYX (int passNo, int asHex) {
2387 for (;;) {
2388 if (isStrArg()) {
2389 int len = 0;
2390 char *res = getStrArg(&len);
2392 if (passNo < 0 || pass == passNo) printf("%s", res);
2393 } else {
2394 int defined = 1;
2395 int32_t v = getExprArg(&defined, NULL);
2397 if (passNo < 0 || pass == passNo) {
2398 if (asHex) printf("%04X", (unsigned int)v);
2399 else printf("%d", v);
2402 if (!eatComma()) break;
2404 return PI_SKIP_LINE;
2408 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2409 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2410 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2411 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2412 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2413 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2415 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2416 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2417 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2420 ///////////////////////////////////////////////////////////////////////////////
2421 // ORG, DISP, etc.
2423 static int piORG (void) {
2424 int defined = 1;
2425 int32_t res = getOneExprArg(&defined, NULL);
2427 if (!defined) fatal("sorry, ORG operand value must be known here");
2428 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
2429 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
2430 pc = disp = res;
2431 if (!wasOrg) {
2432 wasOrg = 1;
2433 ent = res;
2434 if (!wasClr && res > 0) clrAddr = res-1;
2436 return PI_CONT_LINE;
2440 static int piDISP (void) {
2441 int defined = 1;
2442 int32_t res = getOneExprArg(&defined, NULL);
2444 if (!defined) fatal("sorry, DISP operand value must be known here");
2445 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
2446 //printf("DISP=%d\n", res);
2447 disp = res;
2448 return PI_CONT_LINE;
2452 static int piENDDISP (void) {
2453 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2454 checkExprEnd();
2455 disp = pc;
2456 return PI_CONT_LINE;
2460 static int piENT (void) {
2461 int defined = 1;
2462 int32_t res = getOneExprArg(&defined, NULL);
2464 //if (!defined) fatal("sorry, ENT operand value must be known here");
2465 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
2466 ent = res;
2467 return PI_CONT_LINE;
2471 static int piCLR (void) {
2472 int defined = 1;
2473 int32_t res = getOneExprArg(&defined, NULL);
2475 //if (!defined) fatal("sorry, CLR operand value must be known here");
2476 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
2477 clrAddr = res;
2478 wasClr = 1;
2479 return PI_CONT_LINE;
2483 static int piRESERVE (void) {
2485 int defined = 1, start;
2486 int32_t res = getExprArg(&defined, NULL);
2487 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2488 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2489 start = res;
2490 if (!eatComma()) fatal("RESERVE needs 2 args");
2491 res = getOneExprArg(&defined, NULL);
2492 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2493 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2494 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2496 fatal("RESERVE: not yet!");
2497 return PI_CONT_LINE;
2501 static int piALIGN (void) {
2502 int defined = 1;
2503 int32_t res;
2505 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2506 res = getOneExprArg(&defined, NULL);
2507 if (!defined) fatal("sorry, ALIGN operand value must be known here");
2508 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
2509 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
2510 if (res > 0 && pc%res != 0) {
2511 pc /= res;
2512 pc *= res;
2513 pc += res;
2514 disp = pc;
2516 return PI_CONT_LINE;
2520 static int piDISPALIGN (void) {
2521 int defined = 1;
2522 int32_t res = getOneExprArg(&defined, NULL);
2524 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
2525 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
2526 if (res > 0 && disp%res != 0) {
2527 disp /= res;
2528 disp *= res;
2529 disp += res;
2531 return PI_CONT_LINE;
2535 ///////////////////////////////////////////////////////////////////////////////
2536 // DEFx
2538 static int defIncr = 0;
2541 static int piDEFINCR (void) {
2542 int defined = 1;
2543 int32_t cnt = getOneExprArg(&defined, NULL);
2545 if (!defined) fatal("DEFINCR: increment must be defined");
2546 defIncr = cnt;
2547 return PI_CONT_LINE;
2551 static int piDEFBW (int isWord) {
2552 for (;;) {
2553 int defined = 0, fixuptype = UR_FIXUP_NONE;
2555 if (isStrArg()) {
2556 int f, len = 0;
2557 char *res = getStrArg(&len);
2559 for (f = 0; f < len; ++f) {
2560 int32_t b = (uint8_t)res[f];
2561 b += defIncr;
2562 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
2563 if (b < 0) b += 256;
2564 emitByte(b);
2566 } else {
2567 int32_t res = getExprArg(&defined, &fixuptype);
2569 if (pass > 0 && !defined) fatal("undefined operand");
2570 res += defIncr;
2571 if (isWord) {
2572 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
2573 if (res < 0) res += 65536;
2574 if (isWord == 1) {
2575 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 2);
2576 emitWord(res);
2577 } else {
2578 /* reversed word */
2579 switch (fixuptype) {
2580 case UR_FIXUP_WORD: /* swapped bytes */
2581 urasm_fixup_operand(NULL, pc, disp, UR_FIXUP_HIBYTE, 1);
2582 urasm_fixup_operand(NULL, pc, disp+1, UR_FIXUP_LOBYTE, 1);
2583 break;
2584 case UR_FIXUP_LOBYTE:
2585 case UR_FIXUP_HIBYTE:
2586 warningMsg("non-word fixup for reversed word; wtf?!");
2587 break;
2588 default: break;
2590 emitRWord(res);
2592 } else {
2593 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
2594 if (fixuptype != UR_FIXUP_NONE) {
2595 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2596 urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2598 if (res < 0) res += 256;
2599 emitByte(res);
2602 if (!eatComma()) break;
2604 return PI_CONT_LINE;
2607 static int piDEFB (void) { return piDEFBW(0); }
2608 static int piDEFW (void) { return piDEFBW(1); }
2609 static int piDEFR (void) { return piDEFBW(2); }
2612 static int piDEFS (void) {
2613 for (;;) {
2614 int32_t bt, f;
2615 int defined = 0, fixuptype = UR_FIXUP_NONE;
2616 int32_t res = getExprArg(&defined, &fixuptype);
2618 if (pass > 0 && !defined) fatal("undefined operand");
2619 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
2620 if (*currLine && currLine[0] == ',') {
2621 (void)eatComma();
2622 bt = getExprArg(&defined, NULL);
2623 if (pass > 0 && !defined) fatal("undefined operand");
2624 bt += defIncr;
2625 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
2626 if (bt < 0) bt += 256;
2627 if (fixuptype != UR_FIXUP_NONE) {
2628 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2630 for (f = 0; f < res; ++f) {
2631 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2632 emitByte(bt);
2634 } else {
2635 pc += res; disp += res;
2637 if (!eatComma()) break;
2639 return PI_CONT_LINE;
2643 /* bit 0: put '\0' */
2644 /* bit 1: set bit 7 of last byte */
2645 /* bit 2: put length */
2646 static int piDEFSTR (int type) {
2647 for (;;) {
2648 if (isStrArg()) {
2649 int f, len = 0;
2650 char *res = getStrArg(&len);
2652 if (type&0x04) {
2653 if (len > 255) fatal("string too long");
2654 emitByte(len);
2656 for (f = 0; f < len; ++f) {
2657 uint8_t b = (uint8_t)res[f];
2659 if ((type&0x02) && f == len-1) b |= 0x80;
2660 emitByte(b);
2662 if (type&0x01) emitByte(0);
2663 } else {
2664 int defined = 1;
2665 int32_t v = getExprArg(&defined, NULL);
2667 if (pass > 0 && !defined) fatal("undefined expression");
2668 if (!defined) v = 0; else v += defIncr;
2669 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
2670 if (v < 0) v += 256;
2671 emitByte(v);
2673 if (!eatComma()) break;
2675 return PI_CONT_LINE;
2679 static int piDEFM (void) { return piDEFSTR(0x00); }
2680 static int piDEFZ (void) { return piDEFSTR(0x01); }
2681 static int piDEFX (void) { return piDEFSTR(0x02); }
2682 static int piDEFC (void) { return piDEFSTR(0x04); }
2685 ///////////////////////////////////////////////////////////////////////////////
2686 // INCBIN
2688 /* INCBIN "name"[,maxlen] */
2689 static int piINCBIN (void) {
2690 int system = 0;
2691 char *fn, qCh;
2692 uint8_t bt;
2693 FILE *fl;
2694 int maxlen = 65536;
2695 char *args = currLine;
2697 if (!currLine[0]) fatal("INCBIN without file name");
2698 if (isStrArg()) {
2699 qCh = *args++;
2700 system = 0;
2701 } else if (currLine[0] == '<') {
2702 qCh = '>'; ++args;
2703 system = 1;
2704 } else {
2705 qCh = 0;
2706 system = 0;
2708 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2709 if (!fn[0]) fatal("INCBIN: empty file name");
2710 memmove(currLine, args, strlen(args)+1);
2711 // maxlen
2712 if (currLine[0] == ',') {
2713 int defined = 1;
2714 (void)eatComma();
2715 maxlen = getOneExprArg(&defined, NULL);
2716 if (!defined) fatal("INCBIN: undefined maxlen");
2717 if (maxlen < 1) return 1; // nothing to do
2719 // now fix name
2720 char *fname = createIncludeName(fn, system, NULL);
2721 fl = fopen(fname, "rb");
2722 if (!fl) fatal("INCBIN: file not found: '%s'", fname);
2723 while (maxlen-- > 0) {
2724 int res = fread(&bt, 1, 1, fl);
2725 if (!res) break;
2726 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: '%s'", fname); }
2727 emitByte(bt);
2729 fclose(fl);
2730 free(fname);
2731 return PI_SKIP_LINE;
2735 ///////////////////////////////////////////////////////////////////////////////
2736 // INCLUDE
2738 /* INCLUDE "name" */
2739 static int piINCLUDE (void) {
2740 int system = 0;
2741 char *fn, qCh;
2742 char *args = currLine;
2744 if (!currLine[0]) fatal("INCLUDE without file name");
2745 if (isStrArg()) {
2746 qCh = *args++;
2747 system = 0;
2748 } else if (currLine[0] == '<') {
2749 qCh = '>'; ++args;
2750 system = 1;
2751 } else {
2752 qCh = 0;
2753 system = 0;
2755 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2756 if (!fn[0]) fatal("INCLUDE: empty file name");
2758 if (asmTextInclude(fn, system) != 0) fatal("INCLUDE: some shit happens!");
2759 return PI_SKIP_LINE;
2763 ///////////////////////////////////////////////////////////////////////////////
2764 // MODULE, ENDMODULE
2766 static int piENDMODULE (void) {
2767 if (!curModule) fatal("ENDMODULE without MODULE");
2768 if (currLine[0]) {
2769 char *mn = getOneLabelArg();
2770 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
2772 curModule = NULL;
2773 return PI_SKIP_LINE;
2777 static int piMODULE (void) {
2778 ModuleInfo *mi;
2779 char *mn;
2780 SourceLine *ol = curSrcLine;
2781 int inum;
2783 if (curModule) fatal("no nested modules allowed");
2784 mn = getOneLabelArg();
2785 if (!urasm_is_valid_name(mn)) fatal("invalid module name: %s", mn);
2786 mi = moduleFind(mn);
2787 //printf("[%s] %p\n", mn, mi);
2788 if (mi) {
2789 if (mi->seen) {
2790 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
2791 /* skip module */
2792 nextSrcLine(); /* skip "MODULE" line */
2793 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
2794 setCurSrcLine(ol);
2795 fatal("no ENDMODULE");
2797 if (inum == 0) fatal("no nested modules allowed");
2798 curModule = mi;
2799 //skipInstruction(); //k8:wtf?!
2800 return piENDMODULE();
2802 } else {
2803 mi = moduleAdd(mn, curSrcLine->fname);
2805 mi->seen = 1;
2806 curModule = mi;
2807 return PI_SKIP_LINE;
2811 /* Z80, Z80N */
2812 static int piMODEL (void) {
2813 char *mn = getOneIdArgLo();
2814 if (strSkipSpaces(currLine)[0]) fatal("only one model name expected");
2815 if (strcmp(mn, "z80") == 0) urasm_allow_zxnext = 0;
2816 else if (strcmp(mn, "z80a") == 0) urasm_allow_zxnext = 0;
2817 else if (strcmp(mn, "z80n") == 0) urasm_allow_zxnext = 1;
2818 else if (strcmp(mn, "z80next") == 0) urasm_allow_zxnext = 1;
2819 else if (strcmp(mn, "zxnext") == 0) urasm_allow_zxnext = 1;
2820 else fatal("invalid model name: %s", mn);
2821 return PI_SKIP_LINE;
2825 ///////////////////////////////////////////////////////////////////////////////
2826 // DUP, EDUP
2828 static int piEDUP (void) {
2829 fatal("EDUP without DUP");
2830 checkOperatorEnd();
2831 return PI_SKIP_LINE;
2835 static int piDUP (void) {
2836 int defined = 1, dupCnt = 1, inum;
2837 SourceLine *stline, *eline = NULL;
2838 int32_t cnt = getOneExprArg(&defined, NULL);
2840 if (!defined) fatal("DUP: counter must be defined");
2841 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
2842 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
2843 // now find corresponding EDUP
2844 // note that we should skip nested DUPs
2845 nextSrcLine(); // skip ourself
2846 stline = curSrcLine;
2847 while (curSrcLine) {
2848 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
2849 // ok, we found something; what is it?
2850 if (inum == 0) {
2851 // new DUP, skip it
2852 ++dupCnt;
2853 nextSrcLine(); // skip DUP
2854 } else {
2855 // EDUP
2856 if (--dupCnt == 0) {
2857 // gotcha!
2858 eline = curSrcLine;
2859 break;
2861 nextSrcLine(); // skip EDUP
2864 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
2865 // now repeat that lines
2866 while (cnt-- > 0) {
2867 setCurSrcLine(stline);
2868 while (curSrcLine != eline) processCurrentLine();
2870 return PI_SKIP_LINE;
2874 ///////////////////////////////////////////////////////////////////////////////
2875 // IF, ENDIF
2877 static int ifCount = 0;
2880 // results:
2881 // -1: error (should not happen)
2882 // 0: successfully complete
2883 // 1: stopped *AT* "ELSE"
2884 // 2: stopped *AT* "ELSEIF"
2885 static int ifSkipToEndIfOrElse (int wholeBody) {
2886 int inum, wasElse = 0;
2887 SourceLine *oline;
2889 while (curSrcLine) {
2890 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
2891 switch (inum) {
2892 case 0: /* if */
2893 case 6: /* ifx */
2894 nextSrcLine(); // skip IF
2895 ifSkipToEndIfOrElse(1); // and recurse
2896 nextSrcLine(); // skip ENDIF
2897 break;
2898 case 1: /* else */
2899 if (wasElse) fatal("duplicate ELSE");
2900 if (!wholeBody) return 1;
2901 wasElse = 1;
2902 nextSrcLine(); // skip ELSE
2903 break; // and continue
2904 case 2: /* endif */
2905 return 0; // and exit
2906 case 3: /* elif */
2907 case 7: /* elifx */
2908 if (wasElse) fatal("ELSEIF in ELSE");
2909 if (!wholeBody) return 2;
2910 nextSrcLine(); // skip ELSEIF
2911 break; // and continue
2912 case 4: /* macro */
2913 // skip it as a whole
2914 nextSrcLine(); // skip MACRO
2915 for (;;) {
2916 oline = curSrcLine;
2917 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
2918 if (inum == 1) break;
2919 fatal("invalid nested MACRO");
2921 nextSrcLine(); // skip ENDM
2922 break;
2923 case 5: /* endm */
2924 fatal("unexpected ENDM");
2927 fatal("IF without ENDIF");
2928 return -1;
2932 static int piENDIF (void) {
2933 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2934 if (--ifCount < 0) fatal("ENDIF without IF");
2935 checkOperatorEnd();
2936 return PI_SKIP_LINE;
2940 static int piELSE (void) {
2941 if (--ifCount < 0) fatal("ELSE without IF");
2942 nextSrcLine(); // skip ELSE
2943 ifSkipToEndIfOrElse(1);
2944 return PI_SKIP_LINE;
2947 static int piELSEIF (void) {
2948 if (--ifCount < 0) fatal("ELSEIF without IF");
2949 nextSrcLine(); // skip ELSEIF
2950 ifSkipToEndIfOrElse(1);
2951 return PI_SKIP_LINE;
2955 static int piELSEIFX (void) {
2956 if (--ifCount < 0) fatal("ELSEIFX without IF");
2957 nextSrcLine(); // skip ELSEIFX
2958 ifSkipToEndIfOrElse(1);
2959 return PI_SKIP_LINE;
2963 static int piIFAll (int isIfX) {
2964 int defined = 1;
2965 int ooo = lblOptMakeU2;
2966 lblOptMakeU2 = (isIfX ? 1 : 0);
2967 int32_t cond = getOneExprArg(&defined, NULL);
2968 lblOptMakeU2 = ooo;
2970 if (!defined) {
2971 if (!isIfX) fatal("IF: condition must be defined");
2972 cond = 0; // for IFX: 0 if there is any undefined label
2974 if (cond) {
2975 // ok, do it until ELSE/ELSEIF/ENDIF
2976 ++ifCount;
2977 return PI_SKIP_LINE;
2979 for (;;) {
2980 int r;
2981 char *args;
2983 nextSrcLine(); // skip last instruction
2984 // skip until ELSE/ELSEIF/ENDIF
2985 r = ifSkipToEndIfOrElse(0);
2986 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2987 if (r == 0) break; // ENDIF
2988 if (r == 1) { ++ifCount; break; } // ELSE
2989 // ELSEIF, do condition
2990 args = strIsCommand("ELSEIF", currLine);
2991 if (args) {
2992 isIfX = 0;
2993 } else {
2994 if ((args = strIsCommand("ELSEIFX", currLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
2995 isIfX = 1;
2997 memmove(currLine, args, strlen(args)+1);
2998 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2999 cond = getOneExprArg(&defined, NULL);
3000 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
3001 if (!defined) {
3002 if (!isIfX) fatal("ELSEIF: condition must be defined");
3003 cond = 0; // for IFX: 0 if there is any undefined label
3005 if (cond) { ++ifCount; break; } // condition is true
3007 return PI_SKIP_LINE;
3011 static int piIF (void) { return piIFAll(0); }
3012 static int piIFX (void) { return piIFAll(1); }
3015 ///////////////////////////////////////////////////////////////////////////////
3016 // macro processor
3017 ///////////////////////////////////////////////////////////////////////////////
3019 what i did with MACRO is the brain-damaged cheating all the way.
3021 first, i will collect the MACRO body and remember it (removing it
3022 from the main source code, so second pass will not see it).
3024 second, when the macro is used, i will:
3025 * insert the whole macro body in place (label resolver will
3026 fix "..lbl" labels for us)
3027 * let the asm play with it
3029 this is not the best scheme, but it is fairly simple and it works.
3032 // will be called when parser encounters term starting with '=' or '*'
3033 // first term char will not be skipped
3034 // must return pointer to the first char after expression end
3035 static const char *getValueCB (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3036 char name[257];
3037 char *p = name;
3039 if (curmacro == NULL) fatal("'=' outside of macro");
3040 if (*expr++ != '=') fatal("'=' expected!");
3042 expr = strSkipSpaces(expr);
3043 while (*expr) {
3044 if (isAlphaDigit(*expr) || *expr == '_') {
3045 if (p-name > 250) fatal("id too long");
3046 *p++ = *expr++;
3047 continue;
3049 break;
3051 *p = 0;
3053 expr = strSkipSpaces(expr);
3054 for (int f = 0; f < curmacro->mac->argc; ++f) {
3055 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3056 if (*expr == '[') {
3057 urasm_exprval_t v;
3058 int l = (int)strlen(curmacro->argvals[f]);
3059 ++expr; // skip "["
3060 urasm_exprval_init(&v);
3061 expr = urasm_expr_ex(&v, expr, addr, &donteval, defined, error);
3062 if (*error) return expr;
3063 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
3064 ++expr;
3065 if (v.val < 0) v.val += l;
3066 if (v.val < 0 || v.val >= l) {
3067 res->val = '?';
3068 } else {
3069 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
3071 return expr;
3072 } else {
3073 urasm_expr_ex(res, curmacro->argvals[f], addr, &donteval, defined, error);
3074 return expr;
3079 fatal("unknown macro variable: '%s'", name);
3083 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
3084 // opr starts with '=' (invariant)
3085 static int expandCB (char *opr, int oprlen) {
3086 char name[257], *p = name, *op = opr;
3088 if (curmacro == NULL) fatal("'=' outside of macro");
3089 if (*op++ != '=') fatal("'=' expected!"); // just in case
3090 //fprintf(stderr, "expand: [%s]\n", opr);
3092 if (!isAlpha(op[0])) return 0; // nothing to do
3094 // copy argument name
3095 while (*op) {
3096 if (isAlphaDigit(*op) || *op == '_') {
3097 if (p-name > 250) fatal("id too long");
3098 *p++ = *op++;
3099 continue;
3101 break;
3103 *p = 0;
3105 // expand argument? we only need to expand `=arg[n]`
3106 op = strSkipSpaces(op);
3107 if (op[0] != '[') return 0;
3109 for (int f = 0; f < curmacro->mac->argc; ++f) {
3110 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3111 if (*op != '[') abort(); // assertion, just in case
3112 // replace argument with character, or with literal value
3113 // used for `=regpair[0]` or `=regpair[]`
3114 if (op[1] == ']') {
3115 op += 2;
3116 const int l = (int)strlen(curmacro->argvals[f]);
3117 const int lleft = (int)strlen(op);
3118 char *tmp = malloc(l+lleft+8);
3119 snprintf(tmp, l+lleft+8, "%s%s", curmacro->argvals[f], op);
3120 if ((int)strlen(tmp) > oprlen) {
3121 free(tmp);
3122 return -1;
3124 strcpy(opr, tmp);
3125 free(tmp);
3126 return 0;
3127 } else {
3128 urasm_exprval_t v;
3129 int error = 0, defined = 1, donteval = 0;
3130 ++op; // skip '['
3131 urasm_exprval_init(&v);
3132 op = (char *)urasm_expr_ex(&v, op, pc, &donteval, &defined, &error);
3133 if (error) return -1;
3134 // result should be a number
3135 if (op == NULL || *op != ']' || v.str != NULL) return -1;
3136 ++op; // skip ']'
3137 // it is guaranteed to have more than one char in opr
3138 // so we can simply put char from argument value, and remove other expression chars
3139 const int l = (int)strlen(curmacro->argvals[f]);
3140 if (v.val < 0) v.val += l; // negative: indexing from the end
3141 if (v.val < 0 || v.val >= l) fatal("index %d is out of bounds for macro argument '%s'", v.val, curmacro->mac->argnames[f]);
3142 // copy char
3143 opr[0] = curmacro->argvals[f][v.val];
3144 // remove other chars
3145 memmove(opr+1, op, strlen(op)+1);
3147 return 0;
3151 fatal("unknown macro variable: '%s'", name);
3155 // main macro expander
3156 static void processMacro (MacroDef *mc) {
3157 SourceLine *oldcurline = curSrcLine;
3158 CurMacroDef cm;
3160 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3161 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3163 // parse macro arguments
3164 cm.mac = mc;
3165 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) cm.argvals[f] = NULL;
3166 int currArg = 0;
3167 for (;;) {
3168 removeSpaces();
3169 // do we have more arguments?
3170 if (!currLine[0]) break;
3171 // check for argument name (this is purely cosmetic thing)
3172 // use like this: "mcall :argname=[value]" (value may be omited for default)
3173 if (currLine[0] == ':' && isAlpha(currLine[1])) {
3174 int pos = 2;
3175 while (isAlphaDigit(currLine[pos]) || currLine[pos] == '_') ++pos;
3176 //hack!
3177 const char svch = currLine[pos];
3178 currLine[pos] = 0;
3179 if (strcasecmp(currLine+1, mc->argnames[currArg]) != 0) {
3180 // out-of-order, find proper argument and rewind to it
3181 currArg = -1;
3182 for (int c = 0; c < mc->argc; ++c) {
3183 if (strcasecmp(currLine+1, mc->argnames[c]) == 0) {
3184 currArg = c;
3185 break;
3188 if (currArg < 0) fatal("macro '%s' has no argument named '%s'", mc->name, currLine+1);
3190 currLine[pos] = svch;
3191 // remove argument name
3192 memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
3193 removeSpaces();
3194 // check for '='
3195 if (currLine[0] != '=') fatal("expected '=' after argument name");
3196 // remove '='
3197 memmove(currLine, currLine+1, strlen(currLine));
3198 removeSpaces();
3200 // too many args?
3201 if (currArg >= mc->argc) fatal("too many arguments to macro '%s'", mc->name);
3202 // check for default value
3203 if (currLine[0] == ',') {
3204 if (mc->argdefaults[currArg] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[currArg]);
3205 cm.argvals[currArg] = strdup(mc->argdefaults[currArg]);
3206 memmove(currLine, currLine+1, strlen(currLine));
3207 } else {
3208 // skip argument (so we will know its length, and will be able to copy it)
3209 char *e = skipMacroArg(currLine);
3210 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3211 const char ech = *e;
3212 if (ech != 0) {
3213 if (ech == ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc->name);
3214 if (ech != ',') fatal("invalid invocation of macro '%s'", mc->name);
3216 *e = 0;
3217 cm.argvals[currArg] = strdup(currLine);
3218 // strip trailing spaces
3219 strTrimRight(cm.argvals[currArg]);
3220 if (ech) {
3221 *e = ech;
3222 memmove(currLine, e+1, strlen(e));
3223 } else {
3224 currLine[0] = 0;
3227 ++currArg;
3229 // check for line end
3230 removeSpaces();
3231 if (currLine[0]) fatal("invalid macro invocation");
3233 // setup default argument values
3234 for (int f = 0; f < mc->argc; ++f) {
3235 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
3236 if (cm.argvals[f]) continue;
3237 if (mc->argdefaults[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
3238 cm.argvals[f] = strdup(mc->argdefaults[f]);
3239 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
3242 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3244 // insert macro code into the source
3245 setCurSrcLine(mc->lines);
3246 curmacro = &cm;
3247 ++curmacronum;
3248 while (curSrcLine != NULL) {
3249 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3250 processCurrentLine();
3253 setCurSrcLine(oldcurline);
3254 curmacro = NULL;
3255 nextSrcLine();
3259 static int piMACRO (void) {
3260 char *name;
3261 int argc = 0;
3262 char *argdefaults[MAX_MACRO_ARGS];
3263 char *argnames[MAX_MACRO_ARGS];
3264 SourceLine *stline, *eline = NULL;
3265 MacroDef *mc;
3267 name = strdup(getLabelArg(0));
3268 //fprintf(stderr, "[%s]\n", name);
3269 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
3270 if (currLine[0] == ',') memmove(currLine, currLine+1, strlen(currLine));
3271 removeSpaces();
3273 while (currLine[0]) {
3274 if (argc >= MAX_MACRO_ARGS) fatal("too many arguments in MACRO");
3275 if (!isAlpha(currLine[0])) fatal("invalid MACRO definition");
3276 argnames[argc] = strdup(getLabelArg(0));
3277 if (currLine[0] == '=') {
3278 // default value
3279 char *e = strchr(currLine, ','), tch;
3281 if (e == NULL) e = currLine+strlen(currLine);
3282 tch = *e;
3283 *e = 0;
3284 argdefaults[argc] = strdup(currLine+1);
3285 *e = tch;
3286 memmove(currLine, e, strlen(e)+1);
3287 } else {
3288 argdefaults[argc] = NULL;
3290 removeSpaces();
3291 if (currLine[0] == ',') {
3292 memmove(currLine, currLine+1, strlen(currLine));
3293 removeSpaces();
3295 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3296 ++argc;
3299 // now find corresponding ENDM
3300 // note that we should skip nested DUPs
3301 stline = curSrcLine;
3302 nextSrcLine(); // skip ourself
3303 while (curSrcLine) {
3304 int inum;
3306 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
3307 // ok, we found something; what is it?
3308 if (inum == 0) {
3309 // new MACRO
3310 fatal("no nested MACROs yet");
3311 } else {
3312 // ENDM, gotcha!
3313 eline = curSrcLine;
3314 // kill ENDM
3315 eline->line[0] = 0;
3316 nextSrcLine(); // skip ENDM
3317 break;
3320 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
3322 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
3323 mc->name = name;
3324 mc->argc = argc;
3325 for (int f = 0; f < argc; ++f) { mc->argdefaults[f] = argdefaults[f]; mc->argnames[f] = argnames[f]; }
3327 eline->next = NULL;
3328 mc->lines = stline->next;
3329 stline->next = curSrcLine;
3330 stline->line[0] = 0;
3332 mc->next = maclist;
3333 maclist = mc;
3334 setCurSrcLine(stline);
3335 return PI_SKIP_LINE;
3339 static int piENDM (void) {
3340 fatal("ENDM without MACRO");
3341 return PI_SKIP_LINE;
3345 ///////////////////////////////////////////////////////////////////////////////
3346 // line processor
3347 static int optWriteType = 't';
3348 static int optWTChanged = 0;
3351 static void piTapParseLoaderName (void) {
3352 if (eatComma()) {
3353 int len;
3354 if (!isStrArg()) fatal("loader name expected");
3355 char *fn = getStrArg(&len);
3356 if (len > 10) fatal("loader name too long");
3357 memset(tapeLoaderName, ' ', 10);
3358 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
3363 static int piMATHMODE (void) {
3364 char *name = getOneLabelArg();
3365 if (strcasecmp(name, "OLD") == 0) urasm_use_old_priorities = 1;
3366 else if (strcasecmp(name, "NEW") == 0) urasm_use_old_priorities = 0;
3367 else fatal("invalid math mode; NEW or OLD expected");
3368 return PI_SKIP_LINE;
3372 static int piDEFFMT (void) {
3373 char *name;
3375 if (isStrArg()) {
3376 int len = 0;
3377 name = getStrArg(&len);
3378 } else {
3379 name = getLabelArg(1);
3381 if (optWTChanged) return 1;
3383 optRunDMB = optRunTape = optRunSCL = 0;
3384 //optRunSNA = 0;
3386 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA") || !strcasecmp(name, "SNARUN")) {
3387 optWriteType = 's';
3388 if (currLine[0]) fatal("too many expressions");
3389 return PI_SKIP_LINE;
3391 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
3392 optWriteType = 't';
3393 piTapParseLoaderName();
3394 return PI_SKIP_LINE;
3396 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE") || !strcasecmp(name, "TAPERUN")) {
3397 optRunTape = 1;
3398 optWriteType = 't';
3399 piTapParseLoaderName();
3400 return PI_SKIP_LINE;
3402 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
3403 optWriteType = 'r';
3404 if (currLine[0]) fatal("too many expressions");
3405 return PI_SKIP_LINE;
3407 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB") || !strcasecmp(name, "DMBRUN")) {
3408 optRunDMB = (name[3] != 0);
3409 optWriteType = 'd';
3410 if (currLine[0]) fatal("too many expressions");
3411 return PI_SKIP_LINE;
3413 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
3414 optWriteType = 'n';
3415 if (currLine[0]) fatal("too many expressions");
3416 return PI_SKIP_LINE;
3418 if (!strcasecmp(name, "SCL") || !strcasecmp(name, "RUNSCL") || !strcasecmp(name, "SCLRUN")) {
3419 optWriteType = 'S';
3420 optRunSCL = (name[3] != 0);
3421 piTapParseLoaderName();
3422 return PI_SKIP_LINE;
3424 fatal("invalid default output type: %s", name);
3428 ///////////////////////////////////////////////////////////////////////////////
3429 // line processor
3431 static void processCurrentLine (void) {
3432 if (!curSrcLine) return; // do nothing
3433 loadCurSrcLine();
3434 processLabel();
3435 for (;;) {
3436 char *str, *ee, name[66];
3437 UrAsmOp *op;
3438 int len;
3439 const char *errpos;
3441 removeSpacesAndColons();
3442 // skip spaces and ':'
3443 if (!currLine[0]) { nextSrcLine(); break; }
3444 // try to find and process command
3445 str = currLine; //while (*str && isSpace(*str)) ++str; // skip spaces
3446 // find command end
3447 for (ee = str; *ee && !isSpace(*ee) && *ee != '"' && *ee != '\'' && *ee != ',' && *ee != ':'; ++ee) {}
3448 // get command, if any
3449 if (ee != str && ee-str <= 64) {
3450 MacroDef *mc;
3451 memset(name, 0, sizeof(name));
3452 memmove(name, str, ee-str);
3453 /* known command? */
3454 op = urFindOp(name);
3455 if (op) {
3456 // ok, do it
3457 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3458 memmove(currLine, str, strlen(str)+1);
3459 if (op->fn()) {
3460 nextSrcLine(); // skip it
3461 break;
3463 continue;
3465 /* known macro? */
3466 if ((mc = findMacro(name)) != NULL) {
3467 // ok, do it
3468 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3469 memmove(currLine, str, strlen(str)+1);
3470 processMacro(mc);
3471 /* only one macro per line! */
3472 break;
3476 len = urasm_opasm(currLine, pc, disp, &errpos);
3477 if (len < 0) fatalUrLib(len);
3478 pc += len;
3479 disp += len;
3480 if (len >= 0 && errpos) {
3481 memmove(currLine, errpos+1, strlen(errpos));
3482 } else {
3483 nextSrcLine(); // skip it
3484 break;
3490 ///////////////////////////////////////////////////////////////////////////////
3491 // setup instructions
3493 static void registerInstructions (void) {
3494 urAddOp("DISPLAY", piDISPLAY);
3495 urAddOp("DISPLAY0", piDISPLAY0);
3496 urAddOp("DISPLAYA", piDISPLAYA);
3497 urAddOp("DISPHEX", piDISPHEX);
3498 urAddOp("DISPHEX0", piDISPHEX0);
3499 urAddOp("DISPHEXA", piDISPHEXA);
3501 urAddOp("DEFFMT", piDEFFMT);
3502 urAddOp("$MODEL", piMODEL);
3504 urAddOp("MACRO", piMACRO);
3505 urAddOp("ENDM", piENDM);
3507 urAddOp("ORG", piORG);
3508 urAddOp("DISP", piDISP);
3509 urAddOp("ENDDISP", piENDDISP);
3510 urAddOp("PHASE", piDISP);
3511 urAddOp("DEPHASE", piENDDISP);
3512 urAddOp("UNPHASE", piENDDISP);
3513 urAddOp("ALIGN", piALIGN);
3514 urAddOp("DISPALIGN", piDISPALIGN);
3515 urAddOp("PHASEALIGN", piDISPALIGN);
3516 urAddOp("ENT", piENT);
3517 urAddOp("CLR", piCLR);
3518 urAddOp("RESERVE", piRESERVE);
3520 urAddOp("INCLUDE", piINCLUDE);
3521 urAddOp("INCBIN", piINCBIN);
3523 urAddOp("MODULE", piMODULE);
3524 urAddOp("ENDMODULE", piENDMODULE);
3526 urAddOp("DUP", piDUP);
3527 urAddOp("EDUP", piEDUP);
3529 urAddOp("IF", piIF);
3530 urAddOp("IFX", piIFX);
3531 urAddOp("ELSE", piELSE);
3532 urAddOp("ELSEIF", piELSEIF);
3533 urAddOp("ELSEIFX", piELSEIFX);
3534 urAddOp("ENDIF", piENDIF);
3536 urAddOp("DEFINCR", piDEFINCR);
3537 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
3538 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
3539 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
3540 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
3541 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
3542 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
3543 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
3544 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
3546 urAddOp("$ERROR", piERROR);
3547 urAddOp("$WARNING", piWARNING);
3549 urAddOp("$PRINTF", piPRINTF);
3550 urAddOp("$PRINTF0", piPRINTF0);
3551 urAddOp("$PRINTFA", piPRINTFA);
3553 urAddOp("$MATHMODE", piMATHMODE);
3557 ///////////////////////////////////////////////////////////////////////////////
3558 // !0: invalid label
3560 static inline void fnSkipSpaces (const char *expr) {
3561 while (*expr && isSpace(*expr)) ++expr;
3562 return expr;
3567 static inline char fnNextChar (const char *expr) {
3568 while (*expr && isSpace(*expr)) ++expr;
3569 return *expr;
3573 #define FN_SKIP_BLANKS do { \
3574 while (*expr && isSpace(*expr)) ++expr; \
3575 } while (0)
3577 #define FN_CHECK_END do { \
3578 while (*expr && isSpace(*expr)) ++expr; \
3579 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
3580 ++expr; \
3581 } while (0)
3584 #define FN_CHECK_COMMA do { \
3585 while (*expr && isSpace(*expr)) ++expr; \
3586 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
3587 ++expr; \
3588 } while (0)
3591 static const char *readLabelName (char *buf, const char *expr) {
3592 int pos = 0;
3594 while (*expr && isSpace(*expr)) ++expr;
3595 for (;;) {
3596 char ch = *expr++;
3598 if (pos >= 128) return NULL;
3599 if (ch == ')') { --expr; break; }
3600 if (!ch) break;
3601 if (isAlphaDigit(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
3602 buf[pos++] = ch;
3603 } else {
3604 break;
3607 if (pos < 1) return NULL;
3608 buf[pos] = '\0';
3609 if (!urasm_is_valid_name(buf)) return NULL;
3610 return expr;
3614 static const char *fnDefKn (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
3615 char lbl[130];
3617 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
3618 FN_CHECK_END;
3619 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
3620 return expr;
3623 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); }
3624 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); }
3627 static const char *fnAligned256 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3628 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3629 FN_CHECK_END;
3630 if (!donteval) res->val = (res->val%256 ? 0 : 1);
3631 return expr;
3635 static const char *fnSameSeg (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3636 urasm_exprval_t v0, v1;
3638 urasm_exprval_init(&v0);
3639 urasm_exprval_init(&v1);
3640 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
3641 if (*error) return expr;
3642 FN_CHECK_COMMA;
3643 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
3644 if (*error) return expr;
3645 FN_CHECK_END;
3646 if (!donteval) res->val = (v0.val/256 == v1.val/256);
3647 return expr;
3651 static const char *fnAlign (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3652 urasm_exprval_t v0, v1;
3653 urasm_exprval_init(&v0);
3654 urasm_exprval_init(&v1);
3655 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
3656 if (*error) return expr;
3657 FN_SKIP_BLANKS;
3658 if (fnNextChar(expr) == ',') {
3659 FN_CHECK_COMMA;
3660 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
3661 if (*error) return expr;
3662 } else {
3663 v1.val = 256;
3665 FN_CHECK_END;
3666 if (!donteval) {
3667 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
3668 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
3670 return expr;
3674 static const char *fnLow (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3675 const char *ee = expr;
3676 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3677 FN_CHECK_END;
3678 if (!donteval) {
3679 if (res->fixuptype == UR_FIXUP_HIBYTE) {
3680 *error = UR_EXPRERR_FUNC; return ee;
3682 res->fixuptype = UR_FIXUP_LOBYTE;
3683 res->val &= 0xff;
3685 return expr;
3689 static const char *fnHigh (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3690 const char *ee = expr;
3691 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3692 FN_CHECK_END;
3693 if (!donteval) {
3694 if (res->fixuptype == UR_FIXUP_LOBYTE) {
3695 *error = UR_EXPRERR_FUNC; return ee;
3697 res->fixuptype = UR_FIXUP_HIBYTE;
3698 res->val = (res->val>>8)&0xff;
3700 return expr;
3704 static const char *fnAbs (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3705 const char *ee = expr;
3706 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3707 FN_CHECK_END;
3708 if (!donteval) {
3709 if (res->fixuptype != UR_FIXUP_NONE) {
3710 *error = UR_EXPRERR_FUNC; return ee;
3712 res->val = abs(res->val);
3714 return expr;
3718 static const char *fnBSwap (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3719 const char *ee = expr;
3720 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3721 FN_CHECK_END;
3722 if (!donteval) {
3723 if (res->fixuptype != UR_FIXUP_NONE) {
3724 *error = UR_EXPRERR_FUNC; return ee;
3726 res->val = ((res->val>>8)&0xff)|((res->val&0xff)<<8);
3728 return expr;
3732 static const char *fnScrAddr8xn (int nmul, urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3733 int32_t scrbase = 0x4000;
3734 int32_t x, y;
3735 const char *ee = expr;
3736 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
3737 // first argument is `x`
3738 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3739 x = res->val;
3740 // second argument is `y`
3741 FN_CHECK_COMMA;
3742 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3743 y = res->val*nmul;
3744 // optional third arg is screen base
3745 FN_SKIP_BLANKS;
3746 if (expr[0] == ',') {
3747 FN_CHECK_COMMA;
3748 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3749 scrbase = res->val&0xffff;
3751 FN_CHECK_END;
3752 if (!donteval) {
3753 //urasm_exprval_clear(res);
3754 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
3755 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
3756 if (y < 0 || y > 191) fatal("invalid y coordinate: %d", y/nmul);
3757 res->val = scrbase+
3758 (y/64)*2048+
3759 (y%8)*256+
3760 ((y%64)/8)*32+
3762 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
3764 return expr;
3768 static const char *fnScrAddr8x1 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3769 return fnScrAddr8xn(1, res, expr, addr, donteval, defined, error);
3772 static const char *fnScrAddr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3773 return fnScrAddr8xn(8, res, expr, addr, donteval, defined, error);
3777 static const char *fnScrAttr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3778 int32_t scrbase = 0x4000;
3779 int32_t x, y;
3780 const char *ee = expr;
3781 // first argument is `x`
3782 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3783 x = res->val;
3784 // second argument is `y`
3785 FN_CHECK_COMMA;
3786 urasm_exprval_clear(res);
3787 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3788 y = res->val;
3789 // optional third arg is screen base
3790 FN_SKIP_BLANKS;
3791 if (expr[0] == ',') {
3792 FN_CHECK_COMMA;
3793 urasm_exprval_clear(res);
3794 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3795 scrbase = res->val&0xffff;
3797 urasm_exprval_clear(res);
3798 FN_CHECK_END;
3799 if (!donteval) {
3800 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
3801 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
3802 if (y < 0 || y > 23) fatal("invalid y coordinate: %d", y);
3803 res->val = scrbase+6144+y*32+x;
3805 return expr;
3809 static const char *fnMArgToStr (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3810 const char *ee = expr;
3811 // argument is macro argument name
3812 expr = strSkipSpacesConst(expr);
3813 if (expr[0] != '=') { *error = UR_EXPRERR_FUNC; return ee; }
3814 ++expr;
3815 if (!isAlpha(expr[0]) && expr[0] != '_') { *error = UR_EXPRERR_FUNC; return ee; }
3816 const char *nend = expr+1;
3817 while (isAlphaDigit(*nend) || *nend == '_') ++nend;
3818 if (nend-expr > 127) { *error = UR_EXPRERR_FUNC; return ee; }
3819 char name[128];
3820 memset(name, 0, sizeof(name));
3821 memcpy(name, expr, nend-expr);
3822 expr = nend;
3823 // parse index
3824 int index = 0;
3825 int has_index = 0;
3826 expr = strSkipSpacesConst(expr);
3827 if (expr[0] == '[') {
3828 expr = strSkipSpacesConst(expr+1);
3829 if (expr[0] != ']') {
3830 has_index = 1;
3831 int doneg = 0;
3832 if (expr[0] == '+') ++expr;
3833 else if (expr[0] == '-') { doneg = 1; ++expr; }
3834 index = 0;
3835 while (isDigit(expr[0])) {
3836 index = index*10+(expr[0]-'0');
3837 if (index >= 0x1fffffff) fatal("`margtostr` index too high");
3838 ++expr;
3840 expr = strSkipSpacesConst(expr);
3841 if (doneg) index = -index;
3843 if (expr[0] != ']') fatal("`margtostr` invalid index syntax");
3844 ++expr;
3846 // done
3847 FN_CHECK_END;
3848 if (curmacro == NULL) fatal("`margtostr` outside of macro");
3849 if (!donteval) {
3850 for (int f = 0; f < curmacro->mac->argc; ++f) {
3851 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3852 // found argument, convert it to string
3853 if (!has_index) {
3854 res->str = realloc(res->str, strlen(curmacro->argvals[f])+1);
3855 strcpy(res->str, curmacro->argvals[f]);
3856 } else {
3857 const size_t slen = strlen(curmacro->argvals[f]);
3858 if (index < 0) {
3859 index = -index;
3860 if (index > slen) fatal("index out of string");
3861 index = (int)slen-index;
3863 if (index >= slen) fatal("index out of string");
3864 res->str = realloc(res->str, 2);
3865 res->str[0] = curmacro->argvals[f][index];
3866 res->str[1] = 0;
3868 // lowercase it
3869 for (char *s = res->str; *s; ++s) *s = toLower(*s);
3870 //fprintf(stderr, "<%s>\n", res->str);
3871 if (res->str[0]) {
3872 if (res->str[1]) {
3873 res->val = ((unsigned char)res->str[0]);
3874 res->val |= ((unsigned char)res->str[1])<<8;
3875 } else {
3876 res->val = (unsigned char)res->str[0];
3878 } else {
3879 res->val = 0;
3881 return expr;
3884 fatal("unknown macro argument '%s'", name);
3886 return expr;
3890 static const char *fnStrLen (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3891 expr = strSkipSpacesConst(expr);
3892 int stlen = 0;
3893 switch (expr[0]) {
3894 case '"': case '\'': /* string literal? */
3896 char *a = (char *)expr+1;
3897 (void)parseStr(&a, expr[0], &stlen);
3898 expr = (const char *)a;
3900 break;
3901 default: /* expression */
3903 urasm_exprval_t r;
3904 urasm_exprval_init(&r);
3905 expr = urasm_expr_ex(&r, expr, disp, &donteval, defined, error);
3906 if (*error) fatalUrLib(*error);
3907 if (!r.str) fatal("string expected for `strlen()`");
3908 stlen = (int)strlen(r.str);
3909 urasm_exprval_clear(&r);
3911 break;
3913 FN_CHECK_END;
3914 if (!donteval) {
3915 urasm_exprval_setint(res, stlen);
3917 return expr;
3921 static void registerFunctions (void) {
3922 urasm_expr_register_func("defined", fnDefined);
3923 urasm_expr_register_func("known", fnKnown);
3924 urasm_expr_register_func("aligned256", fnAligned256);
3925 urasm_expr_register_func("align", fnAlign);
3926 urasm_expr_register_func("sameseg", fnSameSeg);
3927 urasm_expr_register_func("low", fnLow);
3928 urasm_expr_register_func("high", fnHigh);
3929 urasm_expr_register_func("abs", fnAbs);
3930 urasm_expr_register_func("bswap", fnBSwap);
3932 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8);
3933 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1);
3934 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8);
3936 urasm_expr_register_func("marg2str", fnMArgToStr);
3937 urasm_expr_register_func("strlen", fnStrLen);
3941 ///////////////////////////////////////////////////////////////////////////////
3942 // preparing another pass
3944 static void initPass (void) {
3945 curSrcLine = asmText;
3946 curModule = NULL;
3947 pc = start_pc;
3948 disp = start_disp;
3949 ent = start_ent;
3950 inTapeBlock = 0;
3951 tapeXorB = 0;
3952 wasOrg = 0;
3953 wasClr = 0;
3954 ifCount = 0;
3955 defIncr = 0;
3956 lblOptMakeU2 = 0;
3957 curmacronum = 0;
3958 lastSeenGlobalLabel = strdup(" [MAIN] ");
3959 modulesResetSeen();
3960 prepareMemory();
3961 urasm_use_old_priorities = 0;
3965 static int posstPass (void) {
3966 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
3967 if (checkLabels()) return -1;
3968 if (ifCount != 0) fatal("unbalanced IFs");
3969 return 0;
3973 ///////////////////////////////////////////////////////////////////////////////
3974 static int labelCmp (const void *aa, const void *bb) {
3975 if (aa == bb) return 0;
3976 const UrLabelInfo *a = *(const UrLabelInfo **)aa;
3977 const UrLabelInfo *b = *(const UrLabelInfo **)bb;
3978 return
3979 a->value < b->value ? -1 :
3980 a->value > b->value ? 1 :
3985 static void writeLabelsFile (const char *fname) {
3986 if (!fname || !fname[0]) return;
3987 // count labels
3988 int lcount = 0;
3989 for (const UrLabelInfo *ll = labels; ll; ll = ll->next) ++lcount;
3990 UrLabelInfo **larr = NULL;
3991 if (lcount > 0) {
3992 // create labels array
3993 larr = (UrLabelInfo **)malloc(sizeof(UrLabelInfo *)*lcount);
3994 lcount = 0;
3995 for (UrLabelInfo *ll = labels; ll; ll = ll->next, ++lcount) larr[lcount] = ll;
3996 // sort labels
3997 qsort(larr, lcount, sizeof(UrLabelInfo *), &labelCmp);
3999 // write labels
4000 FILE *fo = fopen(fname, "w");
4001 if (lcount > 0) {
4002 for (int f = 0; f < lcount; ++f) {
4003 UrLabelInfo *ll = larr[f];
4004 if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
4005 if (ll->value < 0) {
4006 fprintf(fo, "%d %s\n", ll->value, ll->name);
4007 } else {
4008 fprintf(fo, "#%04X %s\n", (unsigned)ll->value, ll->name);
4011 free(larr);
4013 fclose(fo);
4017 ///////////////////////////////////////////////////////////////////////////////
4018 // options
4020 static struct option longOpts[] = {
4021 {"org", required_argument, NULL, 600},
4022 {"define", required_argument, NULL, 601},
4023 {"defzero", required_argument, NULL, 602},
4024 {"outdir", required_argument, NULL, 660},
4025 {"reffile", optional_argument, NULL, 669},
4027 {"sna", 0, NULL, 's'},
4028 {"sna128", 0, NULL, 'S'},
4029 {"tap", 0, NULL, 't'},
4030 {"autotap", 0, NULL, 'T'},
4031 {"raw", 0, NULL, 'r'},
4032 {"autodmb", 0, NULL, 'B'},
4033 {"dmb", 0, NULL, 'b'},
4034 {"none", 0, NULL, 'n'},
4035 {"help", 0, NULL, 'h'},
4036 {"hob", 0, NULL, 'H'},
4037 {"scl", 0, NULL, 'l'},
4038 {"autoscl", 0, NULL, 'L'},
4039 {"fixups", optional_argument, NULL, 'F'},
4041 {NULL, 0, NULL, 0}
4045 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
4048 static void usage (const char *pname) {
4049 printf(
4050 "usage: %s [options] infile\n"
4051 "default infiles:", pname);
4052 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
4053 printf("\n"
4054 "options:\n"
4055 " -s --sna write 48K .SNA file with autostart\n"
4056 " -S --sna128 write 148K .SNA file with autostart\n"
4057 " -t --tap write .tap file\n"
4058 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
4059 " -r --raw write raw file(s)\n"
4060 " -b --dmb write DMB file\n"
4061 " -B --autodmb write DMB file with autostart\n"
4062 " -H --hob write HoBeta code file(s)\n"
4063 " -l --scl write SCL TR-DOS archive\n"
4064 " -L --autoscl write autostarting SCL TR-DOS archive\n"
4065 " -n --none write nothing\n"
4066 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
4067 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
4068 " text: .txt file\n"
4069 " asm: address lists with counters\n"
4070 " asmdiff: 2nd and other addresses are relative, with counters\n"
4071 " asmz: address lists, with 0 end markers\n"
4072 " -h --help this help\n"
4073 "specials:\n"
4074 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
4075 " --org xxx set ORG\n"
4076 " --define val perform 'val EQU 1'\n"
4077 " --defzero val perform 'val EQU 0'\n"
4078 " --outdir dir output dir for resulting files (default: current)\n"
4083 ///////////////////////////////////////////////////////////////////////////////
4084 // main
4086 int main (int argc, char *argv[]) {
4087 int res = 0, c;
4088 const char *pname = argv[0];
4089 char *inFile = NULL;
4090 char **defines = NULL, **values = NULL;
4091 int defcount = 0;
4092 int wantref = 0;
4094 initInclideDir();
4096 urasm_getbyte = getByte;
4097 urasm_putbyte = putByte;
4098 urasm_label_by_name = findLabelCB;
4099 urasm_getval = getValueCB;
4100 urasm_expand = expandCB;
4101 urasm_fixup_operand = fixupOperandCB;
4103 //strcpy(tapeLoaderName, "cargador ");
4104 tapeLoaderName[0] = 0;
4106 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
4107 while ((c = getopt_long(argc, argv, "sStTrBbnhHFLl", longOpts, NULL)) >= 0) {
4108 switch (c) {
4109 case '?': return 1;
4110 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
4111 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
4112 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
4113 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
4114 case 'L': optRunSCL = 1; optWriteType = 'S'; optWTChanged = 1; break;
4115 case 'l': optRunSCL = 0; optWriteType = 'S'; optWTChanged = 1; break;
4116 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
4117 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
4118 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
4119 case 'h': usage(pname); res = 0; goto earlyerrquit;
4120 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
4121 case 'F':
4122 optWriteFixups = 1;
4123 if (optarg != NULL) {
4124 if (strcmp(optarg, "asm") == 0 || strcmp(optarg, "zas") == 0) {
4125 optFixupType = 1;
4126 } else if (strcmp(optarg, "asmdiff") == 0 || strcmp(optarg, "zasdiff") == 0) {
4127 optFixupType = 2;
4128 } else if (strcmp(optarg, "asmz") == 0 || strcmp(optarg, "zasz") == 0) {
4129 optFixupType = 3;
4130 } else if (strcmp(optarg, "text") == 0) {
4131 optFixupType = 0;
4132 } else {
4133 fprintf(stderr, "FATAL: invalid fixup type: '%s'\n", optarg);
4134 return 1;
4137 break;
4138 case 600: // org
4139 c = atoi(optarg);
4140 //fprintf(stderr, "ORG: %d\n", c);
4141 if (c < 0 || c > 65535) {
4142 fprintf(stderr, "FATAL: invalid ORG: %d\n", c);
4143 return 1;
4145 start_pc = start_disp = start_ent = c;
4146 break;
4147 case 601: // define
4148 //fprintf(stderr, "define: [%s]\n", optarg);
4149 defines = realloc(defines, sizeof(char *)*(defcount+1));
4150 values = realloc(values, sizeof(char *)*(defcount+1));
4151 defines[defcount] = strdup(optarg);
4152 values[defcount] = strdup("1");
4153 ++defcount;
4154 break;
4155 case 602: // defzero
4156 //fprintf(stderr, "defzero: [%s]\n", optarg);
4157 defines = realloc(defines, sizeof(char *)*(defcount+1));
4158 values = realloc(values, sizeof(char *)*(defcount+1));
4159 defines[defcount] = strdup(optarg);
4160 values[defcount] = strdup("0");
4161 ++defcount;
4162 break;
4163 case 660: // outdir
4164 if (optOutputDir != NULL) free(optOutputDir);
4165 optOutputDir = strdup(optarg);
4166 break;
4167 case 669: // reffile
4168 if (refFileName) free(refFileName);
4169 refFileName = (optarg ? strdup(optarg) : NULL);
4170 wantref = 1;
4171 break;
4175 if (optind >= argc) {
4176 // try to find default input file
4177 for (int f = 0; defInFiles[f]; ++f) {
4178 if (!access(defInFiles[f], R_OK)) {
4179 inFile = strdup(defInFiles[f]);
4180 break;
4183 } else {
4184 inFile = strdup(argv[optind]);
4187 if (!inFile || !inFile[0]) {
4188 res = 1;
4189 fprintf(stderr, "ERROR: no input file!\n");
4190 goto earlyerrquit;
4193 if (optOutputDir == NULL) optOutputDir = strdup(".");
4195 registerInstructions();
4196 registerFunctions();
4198 res = asmTextLoad(inFile, 0);
4199 if (!res) {
4200 for (int f = 0; f < defcount; ++f) {
4201 if (labelDoEQU(defines[f], values[f]) != 0) {
4202 fprintf(stderr, "FATAL: can't define label: '%s'\n", defines[f]);
4203 goto errquit;
4207 for (pass = 0; pass <= 1; ++pass) {
4208 initPass();
4209 printf("pass %d\n", pass);
4210 setCurSrcLine(asmText);
4211 if (setjmp(errJP)) { res = 1; break; }
4212 while (curSrcLine) processCurrentLine();
4213 if (posstPass()) { res = 1; break; }
4216 // write result
4217 if (res == 0) {
4218 char *oc = strdup(inFile);
4219 char *pd = strrchr(oc, '.');
4220 if (pd && !strchr(oc, '/')) *pd = '\0';
4221 switch (optWriteType) {
4222 case 's': saveSna(oc, optSNA48); break;
4223 case 't': saveTap(oc); break;
4224 case 'r': saveRaw(oc); break;
4225 case 'd': saveDMB(oc); break;
4226 case 'H': saveHob(oc); break;
4227 case 'S': saveSCL(oc); break;
4229 free(oc);
4230 if (optWriteFixups) writeFixups();
4231 if (wantref) {
4232 /* build ref file name */
4233 if (!refFileName || !refFileName[0]) {
4234 if (refFileName) free(refFileName);
4235 refFileName = malloc(strlen(inFile)+128);
4236 strcpy(refFileName, inFile);
4237 char *ext = strrchr(refFileName, '.');
4238 if (!ext) {
4239 strcat(refFileName, ".ref");
4240 } else {
4241 #ifdef WIN32
4242 char *slash = NULL;
4243 for (char *ts = refFileName; *ts; ++ts) {
4244 if (*ts == '/' || *ts == '\\') slash = ts;
4246 #else
4247 char *slash = strrchr(refFileName, '/');
4248 #endif
4249 if (!slash || slash < ext) {
4250 strcpy(ext, ".ref");
4251 } else {
4252 strcat(refFileName, ".ref");
4256 writeLabelsFile(refFileName);
4257 printf("refs written to '%s'\n", refFileName);
4258 free(refFileName);
4261 } else {
4262 fprintf(stderr, "ERROR: loading error!\n");
4265 errquit:
4266 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
4267 clearFixups();
4268 urClearLabels();
4269 modulesClear();
4270 asmTextClear();
4271 urClearOps();
4273 earlyerrquit:
4274 if (inFile) free(inFile);
4275 if (sysIncludeDir) free(sysIncludeDir);
4276 for (int f = defcount-1; f >= 0; --f) {
4277 free(values[f]);
4278 free(defines[f]);
4280 if (defines != NULL) { free(values); free(defines); }
4281 if (optOutputDir != NULL) free(optOutputDir);
4282 return (res ? 1 : 0);