added "marg2str"
[urasm.git] / src / urasm.c
blob94d4092bbd4aedeaada713183902ec53a515dded
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;
93 #define MAX_LINE_SIZE 16384
94 static char curLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
97 ///////////////////////////////////////////////////////////////////////////////
98 // init dirs
100 static void initInclideDir (void) {
101 const char *id = getenv("URASM_INCLUDE_DIR");
102 if (id && id[0]) {
103 sysIncludeDir = strdup(id);
104 } else {
105 char myDir[4096];
106 memset(myDir, 0, sizeof(myDir));
107 #ifndef WIN32
108 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
109 strcpy(myDir, ".");
110 } else {
111 char *p = (char *)strrchr(myDir, '/');
112 if (!p) strcpy(myDir, "."); else *p = '\0';
114 #else
115 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
116 char *p = strrchr(myDir, '\\');
117 if (!p) strcpy(myDir, "."); else *p = '\0';
118 #endif
119 strcat(myDir, "/libs");
120 sysIncludeDir = strdup(myDir);
122 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
123 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
127 ///////////////////////////////////////////////////////////////////////////////
128 // string utilities
130 /* trim trailing spaces and comments */
131 static void normalizeStr (char *s) {
132 char *p, inQ = 0;
134 // now skip all shit
135 for (p = s; *p; ++p) {
136 if (inQ) {
137 // inside the string
138 if (*p == '\\') {
139 switch (*(++p)) {
140 case 'x': case 'X': // hex code
141 ++p; // skip 'x'
142 for (int f = 0; f < 2; ++f, ++p) if (!isHexDigit(*p)) break;
143 --p; // last digit will be skiped by 'for'
144 break;
145 case '0': // octal code
146 for (int f = 0; f < 4; ++f, ++p) if (!isDigit(*p) || *p > '7') break;
147 --p; // last digit will be skiped by 'for'
148 break;
149 case '1' ... '9': // decimal code
150 for (int f = 0; f < 3; ++f, ++p) if (!isDigit(*p)) break;
151 --p; // last digit will be skiped by 'for'
152 break;
153 default: ; // other escapes, do nothing
155 } else {
156 if (*p == inQ) inQ = 0;
158 continue;
160 // outside the string
161 switch (*p) {
162 case '"': case '\'': // string catched
163 inQ = *p;
164 break;
165 case ';': // end of line, comment follows
166 *p-- = '\0'; // strip it and quit
167 break;
168 case ':': // reduce colons
169 while (p > s && isSpace(p[-1])) --p;
170 *p = ':';
171 //fprintf(stderr, ":[%s]\n", p);
172 if (p[1] && (p[1] == ':' || isSpace(p[1]))) {
173 char *e, *t;
175 for (e = p+2; *e && (*e == ':' || isSpace(*e)); ++e) {}
176 t = p+1;
177 while ((*t++ = *e++)) {}
178 //fprintf(stderr, "::[%s]\n", p);
180 break;
181 default: ; // do nothing
184 // trim right
185 for (p = s+strlen(s)-1; p >= s && (isSpace(*p) || *p == ':'); --p) {}
186 p[1] = '\0';
190 /* check if string starts with the given command (case-insensitive) */
191 /* returns NULL or pointer to args */
192 /* skips spaces after command if any */
193 static char *strIsCommand (const char *command, char *str) {
194 for (int cnt = 1; cnt > 0; --cnt) {
195 while (*str && isSpace(*str)) ++str; // skip spaces
196 for (; *command && *str; ++command, ++str) {
197 if (toUpper(*command) != toUpper(*str)) return NULL; // alas
199 if (*command) return NULL; // alas
200 if (*str && isAlphaDigit(*str)) return NULL; // alas
201 while (*str && isSpace(*str)) ++str; // skip spaces
202 if (*str && *str == ':') break; // try again if we have a colon
203 return str; // found
205 return NULL;
209 /* parse string literal */
210 /* don't free() result */
211 /* skips trailing spaces */
212 static char *parseStr (char **str, char endQ, int *lenp) {
213 static char buf[MAX_LINE_SIZE];
214 int len = 0, n, f, base;
215 char *a = *str;
217 int xDigit (char ch, int base) {
218 if (ch < '0') return -1;
219 if (base <= 10) {
220 if (ch >= '0'+base) return -1;
221 return ch-'0';
223 ch = toUpper(ch);
224 if (ch <= '9') return ch-'0';
225 if (ch < 'A' || ch > 'A'+base-10) return -1;
226 ch -= 'A'-10;
227 return (ch < base ? ch : -1);
230 memset(buf, 0, sizeof(buf));
231 if (lenp) *lenp = 0;
232 for (; *a; ++a) {
233 if (*a == '\\') {
234 if (!a[1]) break;
235 switch (*(++a)) {
236 case 'a': buf[len++] = '\a'; break;
237 case 'b': buf[len++] = '\b'; break;
238 case 'e': buf[len++] = '\x1b'; break;
239 case 'f': buf[len++] = '\f'; break;
240 case 'n': buf[len++] = '\n'; break;
241 case 'r': buf[len++] = '\r'; break;
242 case 't': buf[len++] = '\t'; break;
243 case 'v': buf[len++] = '\v'; break;
244 case 'z': buf[len++] = '\0'; break;
245 case 'x': case 'X': // hex
246 ++a; // skip 'x'
247 base = 16; f = 2;
248 donum: for (n = 0; f > 0; --f) {
249 char ch = xDigit(*a++, base);
251 if (ch < 0) { --a; break; }
252 n *= base;
253 n += ch;
255 buf[len++] = n;
256 --a; // return to the last digit, 'for' will skip it
257 break;
258 case '0': // octal
259 base = 8; f = 4;
260 goto donum;
261 case '1' ... '9': // decimal
262 base = 10; f = 3;
263 goto donum;
264 default: buf[len++] = a[0]; break; // others
266 } else {
267 if (*a == endQ) { ++a; break; }
268 buf[len++] = *a;
271 while (*a && isSpace(*a)) ++a; // skip trailing spaces
272 *str = a;
273 buf[len] = '\0';
274 if (lenp) *lenp = len;
275 return buf;
279 ///////////////////////////////////////////////////////////////////////////////
280 // source file stack, reader, etc
282 typedef struct SourceLine {
283 struct SourceLine *next;
284 char *line;
285 char *fname;
286 int lineNo;
287 } SourceLine;
289 static SourceLine *asmText = NULL;
290 static SourceLine *asmTextLast = NULL;
291 static SourceLine *curSrcLine = NULL;
294 typedef struct MacroDef {
295 struct MacroDef *next;
296 char *name;
297 SourceLine *lines;
298 int argc;
299 char *argdefaults[32]; // default values
300 char *argnames[32]; // argument names
301 } MacroDef;
303 typedef struct {
304 MacroDef *mac;
305 char *argvals[32]; // argument values
306 } CurMacroDef;
308 static MacroDef *maclist = NULL;
309 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
310 static int curmacronum = 0;
313 static void asmTextClear (void) {
314 while (asmText) {
315 SourceLine *l = asmText;
317 asmText = asmText->next;
318 free(l->line);
319 free(l->fname);
320 free(l);
322 asmTextLast = curSrcLine = NULL;
326 static int asmTextLoad (const char *fname) {
327 FILE *fl;
328 int lineNo = 0;
329 SourceLine *s;
331 if (!(fl = fopen(fname, "r"))) {
332 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
333 return -1;
335 printf("loading: %s\n", fname);
336 // read file
337 while (fgets(curLine, sizeof(curLine)-1, fl)) {
338 ++lineNo;
339 curLine[sizeof(curLine)-1] = '\0';
340 normalizeStr(curLine);
341 //fprintf(stderr, "*[%s]\n", curLine);
342 if (!curLine[0]) continue; // don't store empty lines
343 // add current line
344 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
345 s->lineNo = lineNo;
346 if ((s->line = strdup(curLine)) == NULL) abort();
347 if ((s->fname = strdup(fname)) == NULL) abort();
348 if (asmTextLast) asmTextLast->next = s; else asmText = s;
349 asmTextLast = s;
351 fclose(fl);
352 return 0;
356 static inline void loadCurSrcLine (void) { if (curSrcLine) strcpy(curLine, (curSrcLine != NULL ? curSrcLine->line : "")); }
357 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
358 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
361 static int includeCount = 0;
364 static void normIncName (char *dest, const char *fn, int system) {
365 struct stat st;
367 if (system) sprintf(dest, "%s/%s", sysIncludeDir, fn); else sprintf(dest, "%s", fn);
368 if (stat(dest, &st)) return;
369 if (S_ISDIR(st.st_mode)) strcat(dest, "/zzmain.zas");
373 // process 'INCLUDE'
374 // include file instead of the current line
375 static int asmTextInclude (const char *fname, int system) {
376 char *fn;
377 FILE *fl;
378 int lineNo = 0;
379 SourceLine *first = NULL, *last = NULL, *s = NULL;
381 if (includeCount > 256) {
382 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine->fname, curSrcLine->lineNo);
383 return -1;
386 fn = strdup(fname);
387 normIncName(curLine, fn, system);
388 free(fn);
389 fn = alloca(strlen(curLine+1));
390 strcpy(fn, curLine);
391 ++includeCount;
392 printf("loading: %s\n", fn);
393 if ((fl = fopen(fn, "r")) == NULL) {
394 fprintf(stderr, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine->fname, curSrcLine->lineNo, curLine);
395 return -1;
398 while (fgets(curLine, sizeof(curLine)-1, fl)) {
399 ++lineNo;
400 curLine[sizeof(curLine)-1] = '\0';
401 normalizeStr(curLine);
402 if (!curLine[0]) continue; // don't store empty lines
403 // add current line
404 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
405 s->lineNo = lineNo;
406 if ((s->line = strdup(curLine)) == NULL) abort();
407 if ((s->fname = strdup(fn)) == NULL) abort();
408 if (last != NULL) last->next = s; else first = s;
409 last = s;
411 fclose(fl);
412 --includeCount;
413 // now replace current line
415 free(curSrcLine->line);
416 free(curSrcLine->fname);
417 if (last != NULL) {
418 curSrcLine->line = first->line;
419 curSrcLine->fname = first->fname;
420 curSrcLine->lineNo = first->lineNo;
421 if ((first = first->next) != NULL) {
422 // more than one line
423 last->next = curSrcLine->next;
424 curSrcLine->next = first;
428 curSrcLine->line[0] = 0;
429 last->next = curSrcLine->next;
430 curSrcLine->next = first;
431 return 0;
435 ///////////////////////////////////////////////////////////////////////////////
436 // prototypes
438 static void processCurrentLine (void); // only one, will skip to next one
441 ///////////////////////////////////////////////////////////////////////////////
442 // error raisers, etc
444 static jmp_buf errJP;
447 static void errorWriteFile (FILE *fo) {
448 if (curSrcLine) {
449 fprintf(fo, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
450 } else {
451 fprintf(fo, "somewhere in time: ");
455 static void errorMsgV (const char *fmt, va_list ap) {
456 errorWriteFile(stderr);
457 vfprintf(stderr, fmt, ap);
458 va_end(ap);
459 fputc('\n', stderr);
460 fflush(stderr);
464 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
465 va_list ap;
466 fprintf(stderr, "WARNING ");
467 va_start(ap, fmt);
468 errorMsgV(fmt, ap);
472 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
473 va_list ap;
474 fprintf(stderr, "FATAL ");
475 va_start(ap, fmt);
476 errorMsgV(fmt, ap);
480 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
481 va_list ap;
482 va_start(ap, fmt);
483 errorMsgV(fmt, ap);
484 longjmp(errJP, 666);
488 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
489 errorMsg("%s", urasm_errormsg(errcode));
490 longjmp(errJP, 666);
494 //////////////////////////////////////////////////////////////////////////////
495 // operator management
497 // return !0 to skip current line
498 typedef int (*UrAsmOpFn) (void);
500 enum {
501 PI_CONT_LINE = 0,
502 PI_SKIP_LINE = 1
505 typedef struct UrAsmOp {
506 char *name;
507 UrAsmOpFn fn;
508 struct UrAsmOp *next;
509 } UrAsmOp;
511 static UrAsmOp *oplist = NULL;
514 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
515 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
516 if (!res) abort();
517 res->name = strdup(name);
518 res->fn = fn;
519 res->next = oplist;
520 oplist = res;
521 return res;
525 static UrAsmOp *urFindOp (const char *name) {
526 UrAsmOp *res;
527 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
528 return res;
532 static void urClearOps (void) {
533 while (oplist) {
534 UrAsmOp *c = oplist;
536 oplist = oplist->next;
537 free(c->name);
538 free(c);
543 ///////////////////////////////////////////////////////////////////////////////
544 // label management
546 typedef struct UrLabelInfo {
547 char *name;
548 int32_t value;
549 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
550 int known; /* !0: label value already known */
551 int refLine; /* first referenced line */
552 int fixuptype; /* UR_FIXUP_XXX */
553 char *refFile;
554 struct UrLabelInfo *next;
555 } UrLabelInfo;
557 static UrLabelInfo *labels = NULL;
560 static void urClearLabels (void) {
561 UrLabelInfo *c;
563 while ((c = labels) != NULL) {
564 labels = c->next;
565 if (c->name) free(c->name);
566 if (c->refFile) free(c->refFile);
567 free(c);
572 static UrLabelInfo *urFindLabel (const char *name) {
573 for (UrLabelInfo *c = labels; c; c = c->next) if (strcmp(name, c->name) == 0) return c;
574 return NULL;
578 static UrLabelInfo *urAddLabel (const char *name) {
579 UrLabelInfo *c = urFindLabel(name);
581 if (c == NULL) {
582 UrLabelInfo *p;
584 for (p = NULL, c = labels; c; p = c, c = c->next) {}
585 c = calloc(1, sizeof(UrLabelInfo));
586 if (!c) abort();
587 c->name = strdup(name);
588 c->type = -1;
589 c->fixuptype = UR_FIXUP_NONE;
590 if (p) p->next = c; else labels = c;
591 c->next = NULL;
593 return c;
597 ///////////////////////////////////////////////////////////////////////////////
598 // module list management
600 typedef struct ModuleInfo {
601 char *name;
602 char *fname; // opened in this file
603 int seen; // !0: module already seen, skip other definitions from the same file
604 struct ModuleInfo *next;
605 } ModuleInfo;
607 static ModuleInfo *modules = NULL;
608 static ModuleInfo *curModule = NULL;
611 static void modulesClear (void) {
612 curModule = NULL;
613 while (modules) {
614 ModuleInfo *c = modules;
616 modules = modules->next;
617 free(c->name);
618 free(c->fname);
619 free(c);
624 static void modulesResetSeen (void) {
625 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
629 static ModuleInfo *moduleFind (const char *name) {
630 if (!name || !name[0]) return NULL;
631 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
632 return NULL;
636 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
637 ModuleInfo *c;
639 if (!name || !fname || !name[0] || !fname[0]) abort();
640 if ((c = calloc(1, sizeof(ModuleInfo))) == NULL) abort();
641 if ((c->name = strdup(name)) == NULL) abort();
642 if ((c->fname = strdup(fname)) == NULL) abort();
643 c->next = modules;
644 return (modules = c);
648 ///////////////////////////////////////////////////////////////////////////////
649 // fixup management
650 typedef struct FixupItem {
651 struct FixupItem *next;
652 uint16_t opdestaddr;
653 uint16_t opaddr;
654 int fixuptype;
655 int size;
656 } FixupItem;
657 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
660 static void clearFixups (void) {
661 FixupItem *c;
663 while ((c = fixlisthead) != NULL) {
664 fixlisthead = c->next;
665 free(c);
670 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
671 FixupItem *fx = calloc(1, sizeof(FixupItem));
673 fx->opdestaddr = opdestaddr;
674 fx->opaddr = opaddr;
675 fx->fixuptype = fixuptype;
676 fx->size = size;
678 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
679 fx->next = NULL;
680 fixlisttail = fx;
684 ///////////////////////////////////////////////////////////////////////////////
685 // destination memory management
687 static uint8_t memory[65536];
688 static char memused[65536];
689 static char memresv[65536];
690 static uint16_t start_pc = 0x100; // viva CP/M!
691 static uint16_t start_disp = 0x100; // viva CP/M!
692 static uint16_t start_ent = 0x100; // viva CP/M!
693 static uint16_t pc = 0; /* current position to write */
694 static uint16_t disp = 0; /* current 'virtual PC' */
695 static uint16_t ent = 0; /* starting address */
696 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
697 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
698 static int inTapeBlock = 0;
699 static uint8_t tapeXorB = 0;
702 static inline uint8_t getByte (uint16_t addr) {
703 return memory[addr];
708 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
709 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
714 static inline void putByte (uint16_t addr, uint8_t b) {
715 if (inTapeBlock) tapeXorB ^= b;
716 memory[addr] = b;
717 memused[addr] = 1;
721 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
722 putByte(addr, w&0xFFU);
723 putByte(addr+1, (w>>8)&0xFFU);
727 static inline void emitByte (uint8_t b) {
728 putByte(pc, b);
729 ++pc;
730 ++disp;
734 static inline void emitWord (uint16_t w) {
735 emitByte(w&0xFFU);
736 emitByte((w>>8)&0xFFU);
740 static inline void emitRWord (uint16_t w) {
741 emitByte((w>>8)&0xFFU);
742 emitByte(w&0xFFU);
746 static void prepareMemory (void) {
747 memset(memory, 0, sizeof(memory));
748 memset(memused, 0, sizeof(memused));
749 memset(memresv, 0, sizeof(memresv));
753 ///////////////////////////////////////////////////////////////////////////////
754 // label getter and utilities
756 static char *lastSeenGlobalLabel = NULL; /* global */
759 static char *fixLocalLabel (const char *name) {
760 static char newname[MAX_LINE_SIZE*2+1024];
762 memset(newname, 0, sizeof(newname));
763 if (!name || !name[0]) {
764 newname[0] = '\0';
765 } else if (!lastSeenGlobalLabel || name[0] != '.') {
766 strcpy(newname, name);
767 } else {
768 if (name[0] == '.' && name[1] == '.') {
769 // this is macro label
770 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
771 sprintf(newname, "{#MAC%d:%s:%s}", curmacronum, curmacro->mac->name, name);
772 } else {
773 // this is local label, let's rename it
774 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
776 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
778 return newname;
782 static char *fixGlobalLabel (const char *name) {
783 static char newname[MAX_LINE_SIZE*2+1024];
785 memset(newname, 0, sizeof(newname));
786 if (!name || !name[0]) {
787 newname[0] = '\0';
788 } else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
789 if (name[0] == '@' && name[1]) ++name;
790 strcpy(newname, name);
791 } else {
792 // this is global unqualified label and we have a module; let's rename it
793 sprintf(newname, "%s.%s", curModule->name, name);
795 //printf("%s --> %s\n", name, newname);
796 return newname;
800 static int lblOptMakeU2 = 0;
802 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
803 UrLabelInfo *lbl;
804 char *ln, *nn;
806 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
807 lbl = urFindLabel(nn);
808 if (!lbl) {
809 // try non-module label
810 lbl = urFindLabel(ln);
812 if (!lbl) {
813 if (pass != 0) {
814 errorMsg("using undefined label %s", ln);
815 *found = 0;
816 *defined = 0;
817 return 0;
819 lbl = urAddLabel(nn);
820 lbl->type = (lblOptMakeU2 ? -42 : -1);
821 lbl->known = 0;
822 lbl->refLine = curSrcLine->lineNo;
823 lbl->refFile = strdup(curSrcLine->fname);
824 //printf("new label: [%s]\n", lbl->name);
825 } else {
826 //printf("label reference: [%s]\n", lbl->name);
828 if (lbl) {
829 *found = 1;
830 *defined = lbl->known!=0;
831 *fixuptype = lbl->fixuptype;
832 return lbl->value;
834 *found = 0;
835 *defined = 0;
836 return 0;
840 // qtypes
841 enum {
842 UR_QTYPE_DEFINED,
843 UR_QTYPE_KNOWN
846 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
847 UrLabelInfo *lbl;
848 char *ln, *nn;
850 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
851 lbl = urFindLabel(nn);
852 if (!lbl) {
853 // try non-module label
854 lbl = urFindLabel(ln);
856 switch (qtype) {
857 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
858 case UR_QTYPE_KNOWN: return lbl!=NULL;
859 default: ;
861 return 0;
865 static void fixupOperandCB (const urasm_operand_t *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
866 if (pass == 1) {
867 //static const char *n[4] = {"none", "word", "low", "high"};
868 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
869 addFixup(opdestaddr, opaddr, fixuptype, size);
874 static int checkLabels (void) {
875 int wasError = 0;
877 for (UrLabelInfo *c = labels; c; c = c->next) {
878 if (c->type == -1) {
879 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
880 wasError = 1;
882 if (c->type == 0) c->known = -1;
884 //if (wasError) longjmp(errJP, 667);
885 return wasError;
889 ///////////////////////////////////////////////////////////////////////////////
890 // expression utils (aka string parsing)
893 /* skip leading spaces */
894 /* returns string with spaces skipped */
895 static inline char *strSkipSpaces (const char *s) {
896 while (*s && isSpace(*s)) ++s;
897 return (char *)s;
901 /* skip leading spaces and colons */
902 /* returns string with spaces skipped */
903 static inline char *strSkipSpacesColons (char *s) {
904 while (*s && (isSpace(*s) || *s == ':')) ++s;
905 return s;
909 /* remove leading spaces from the current line */
910 static inline void removeSpaces (void) {
911 char *e = strSkipSpaces(curLine);
912 if (e != curLine) memmove(curLine, e, strlen(e)+1);
916 /* skip leading spaces, and argument (up to, but not including comma or colon) */
917 /* correctly skip strings */
918 /* returns string after skipped argument (with trailing spaces skipped) */
919 static char *skipArg (char *str) {
920 for (;;) {
921 str = strSkipSpaces(str);
922 if (!str[0] || str[0] == ',' || str[0] == ':') return str;
923 if (str[0] == '"' || str[0] == '\'') {
924 const char qch = *str++;
925 while (*str && *str != qch) {
926 if (*str++ == '\\') {
927 switch (*str++) {
928 case 'x': case 'X':
929 for (int f = 0; f < 2; ++f, ++str) if (!isHexDigit(*str)) break;
930 break;
931 case '0':
932 for (int f = 0; f < 4; ++f, ++str) if (*str < '0' || *str > '7') break;
933 break;
934 case '1' ... '9':
935 for (int f = 0; f < 3; ++f, ++str) if (!isDigit(*str)) break;
936 break;
940 continue;
942 ++str;
947 /* evaluate next numeric expression in input string */
948 /* returns expression value */
949 static int32_t getExprArg (int *defined, int *addr) {
950 int error = 0;
951 char *a = strSkipSpaces(curLine);
952 int32_t res;
953 if (!a[0]) fatal("expression expected");
954 const char *ee = urasm_expr(&res, a, disp, defined, addr, &error);
955 if (error) fatalUrLib(error);
956 if (*ee) {
957 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
958 memmove(curLine, ee, strlen(ee)+1);
959 } else {
960 curLine[0] = '\0';
962 return res;
966 /* evaluate next string expression in input string */
967 /* returns expression value */
968 static char *getStrExprArg (void) {
969 int error = 0;
970 int donteval = 0, defined = 0;
971 static char resbuf[256];
972 char *a = strSkipSpaces(curLine);
973 if (!a[0]) fatal("expression expected");
974 urasm_exprval_t res;
975 urasm_exprval_init(&res);
976 const char *ee = urasm_expr_ex(&res, a, disp, &donteval, &defined, &error);
977 if (error) fatalUrLib(error);
978 if (*ee) {
979 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
980 memmove(curLine, ee, strlen(ee)+1);
981 } else {
982 curLine[0] = '\0';
984 if (res.str) {
985 snprintf(resbuf, sizeof(resbuf), "%s", res.str);
986 } else {
987 snprintf(resbuf, sizeof(resbuf), "%d", res.val);
989 urasm_exprval_clear(&res);
990 return resbuf;
994 /* evaluate next numeric expression in input string */
995 /* there shoild be no other expressions in the string */
996 /* returns expression value */
997 static int32_t getOneExprArg (int *defined, int *addr) {
998 int32_t res = getExprArg(defined, addr);
999 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
1000 return res;
1004 /* is next expression a string literal? */
1005 static inline int isStrArg (void) {
1006 const char *s = strSkipSpaces(curLine);
1007 return (s[0] == '"' || s[0] == '\'');
1011 /* check of we reached end of operator */
1012 static __attribute__((unused)) inline int isOperatorEnd (void) {
1013 const char *s = strSkipSpaces(curLine);
1014 return (s[0] == 0 || s[0] == ':');
1018 /* check of we reached end of operator */
1019 static __attribute__((unused)) inline int isLineEnd (void) {
1020 const char *s = strSkipSpaces(curLine);
1021 return (s[0] == 0);
1025 /* parse string argument from input string */
1026 /* returns parsed string */
1027 static char *getStrArg (int *lenp) {
1028 char *res, qCh;
1029 char *a = strSkipSpaces(curLine);
1030 qCh = *a++;
1031 if (qCh != '"' && qCh != '\'') fatal("string expected");
1032 res = parseStr(&a, qCh, lenp);
1033 if (*a) {
1034 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
1035 memmove(curLine, a, strlen(a)+1);
1036 } else {
1037 curLine[0] = '\0';
1039 return res;
1043 /* get identifier (and lowercase it) */
1044 static char *getOneIdArgLo (void) {
1045 static char res[MAX_LINE_SIZE+128];
1046 char *p;
1047 char *a = strSkipSpaces(curLine);
1048 memset(res, 0, sizeof(res));
1049 if (!a[0]) fatal("identifier expected");
1050 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1051 for (; p > res && isSpace(p[-1]); --p) {}
1052 *p = '\0';
1053 if (p-res > 120) fatal("identifier too long: %s", res);
1054 while (*a && isSpace(*a)) ++a;
1055 if (*a) {
1056 memmove(curLine, a, strlen(a)+1);
1057 if (curLine[0] == ';') curLine[0] = 0;
1058 if (curLine[0]) fatal("extra arguments");
1059 } else {
1060 curLine[0] = '\0';
1062 for (char *t = res; *t; ++t) *t = toLower(*t);
1063 return res;
1067 /* get label argument */
1068 static char *getLabelArg (int checkdelim) {
1069 static char res[MAX_LINE_SIZE+128];
1070 char *p;
1071 char *a = strSkipSpaces(curLine);
1072 memset(res, 0, sizeof(res));
1073 if (!a[0]) fatal("label expected");
1074 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1075 for (; p > res && isSpace(p[-1]); --p) {}
1076 *p = '\0';
1077 if (p-res > 120) fatal("label name too long: %s", res);
1078 while (*a && isSpace(*a)) ++a;
1079 if (*a) {
1080 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
1081 memmove(curLine, a, strlen(a)+1);
1082 } else {
1083 curLine[0] = '\0';
1085 return res;
1089 /* get label argument, and ensure that it is the last one */
1090 static char *getOneLabelArg (void) {
1091 char *res = getLabelArg(1);
1092 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
1093 return res;
1097 /* returns ',' or 0 */
1098 static char eatComma (void) {
1099 char *a = strSkipSpaces(curLine);
1100 if (!a[0]) { curLine[0] = '\0'; return 0; }
1101 if (a[0] == ':') return 0;
1102 if (a[0] != ',') fatal("invalid expression: ',' expected");
1103 for (++a; *a && isSpace(*a); ++a) {}
1104 if (!a[0]) { curLine[0] = '\0'; return 0; }
1105 memmove(curLine, a, strlen(a)+1);
1106 return ',';
1110 ///////////////////////////////////////////////////////////////////////////////
1111 // label processor
1113 static MAYBE_UNUSED void removeSpacesAndColons (void) {
1114 char *ep = strSkipSpacesColons(curLine);
1115 memmove(curLine, ep, strlen(ep)+1);
1119 static void checkExprEnd (void) {
1120 char *ep = strSkipSpaces(curLine);
1121 memmove(curLine, ep, strlen(ep)+1);
1122 if (curLine[0] && curLine[0] != ':') fatal("end of expression expected");
1126 static void checkOperatorEnd (void) {
1127 char *ep = strSkipSpaces(curLine);
1128 memmove(curLine, ep, strlen(ep)+1);
1129 if (curLine[0]) fatal("end of operator expected");
1133 /* remove label from curLine */
1134 static void removeLabel (void) {
1135 char *ep = curLine;
1136 if (ep[0] && !isSpace(ep[0])) for (ep = curLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {} // skip text
1137 // skip spaces and colons
1138 ep = strSkipSpacesColons(ep);
1139 memmove(curLine, ep, strlen(ep)+1);
1143 static int labelDoEQU (const char *lblname, const char *value) {
1144 static char n2[256];
1145 UrLabelInfo *lbl;
1147 if (value == NULL || lblname == NULL || !lblname[0] || strlen(lblname) > 255 || strlen(value) >= MAX_LINE_SIZE) return -1;
1148 memset(n2, 0, sizeof(n2));
1149 strcpy(n2, lblname);
1150 if (!urasm_is_valid_name(n2)) return -1; // this is not a valid label, get out of here
1151 // check if this can be an instruction
1152 lbl = urAddLabel(lblname);
1153 if (!lbl->refFile) {
1154 lbl->refLine = 0;
1155 lbl->refFile = strdup("artificially-defined-label");
1158 strcpy(curLine, value);
1161 int defined = 1, addr = UR_FIXUP_NONE;
1162 int32_t res = getOneExprArg(&defined, &addr);
1164 lbl->type = 1; // equ label
1165 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1166 if (defined) {
1167 lbl->value = res;
1168 lbl->known = 1;
1169 } else {
1170 return -1; //fatal("can't calculate label %s", lbl->name);
1173 return 0;
1177 static void processLabel (void) {
1178 char *argstart;
1179 char *ep, *ln, *nn;
1180 static char n2[256];
1181 UrLabelInfo *lbl;
1182 int noLocAff = 0, doEQU = 0;
1183 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1184 memset(n2, 0, sizeof(n2));
1185 if (!curLine[0] || isSpace(curLine[0]) || curLine[0] == ':') {
1186 // this may be " id = smth" or " id equ smth"
1187 ep = curLine;
1188 // skip spaces
1189 while (isSpace(*ep)) ++ep;
1190 if (ep[0] == ':' || !ep[0] || (!isAlpha(ep[0]) && ep[0] != '_' && ep[0] != '.' && ep[0] != '@')) {
1191 removeLabel(); // removeLabel() removes any spaces, etc
1192 return;
1194 // this looks like a label; check for '=' or 'equ'
1195 while (isAlphaDigit(ep[0]) || ep[0] == '_' || ep[0] == '.' || ep[0] == '@') ++ep;
1196 nn = ep;
1197 // skip trailing spaces
1198 while (isSpace(*ep)) ++ep;
1199 if (ep[0] == '=') {
1200 doEQU = 0;
1201 argstart = ++ep;
1202 } else if (isSpace(*nn)) {
1203 doEQU = 1;
1204 argstart = strIsCommand("EQU", ep);
1205 } else {
1206 argstart = NULL;
1208 if (!argstart) {
1209 removeLabel(); // removeLabel() removes any spaces, etc
1210 return;
1212 // remove leading spaces from name
1213 // copy label
1214 ep = curLine;
1215 // skip spaces
1216 while (isSpace(*ep)) ++ep;
1217 if (ep >= nn) fatal("internal compiler error");
1218 if (nn-ep > 120) fatal("label too long");
1219 memset(n2, 0, sizeof(n2));
1220 memmove(n2, ep, nn-ep);
1221 if (urFindOp(n2) || !urasm_is_valid_name(n2)) {
1222 //fatal("invalid label name");
1223 removeLabel(); // removeLabel() removes any spaces, etc
1224 return;
1226 // remove label name
1227 memmove(curLine, argstart, strlen(argstart)+1);
1228 // append label
1229 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1230 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1231 lbl = urAddLabel(nn);
1232 if (!lbl->refFile) {
1233 lbl->refLine = curSrcLine->lineNo;
1234 lbl->refFile = strdup(curSrcLine->fname);
1236 if (doEQU && pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1237 int defined = 1, addr = UR_FIXUP_NONE;
1238 int32_t res = getOneExprArg(&defined, &addr);
1239 lbl->type = doEQU;
1240 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1241 if (defined) {
1242 lbl->value = res;
1243 lbl->known = 1;
1244 } else {
1245 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1247 curLine[0] = '\0';
1248 return;
1250 // collect label
1251 for (ep = curLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {}
1252 if (ep-curLine > 120) fatal("label too long");
1253 // copy label
1254 memset(n2, 0, sizeof(n2));
1255 memmove(n2, curLine, ep-curLine);
1256 if (urFindOp(n2)) {
1257 ep = strSkipSpaces(ep);
1258 if (*ep != ':') return; // this must be an instruction, process it
1260 if (!urasm_is_valid_name(n2)) return; // this is not a valid label, get out of here
1261 // check if this can be instruction
1262 //ep = strSkipSpaces(ep);
1263 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1264 // ok, we got a good label
1265 removeLabel();
1266 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1267 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1268 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1269 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1270 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1271 lbl = urAddLabel(nn);
1272 if (!lbl->refFile) {
1273 lbl->refLine = curSrcLine->lineNo;
1274 lbl->refFile = strdup(curSrcLine->fname);
1276 //printf("new: [%s]\n", lbl->name);
1277 // get command name
1278 if (curLine[0] == '=') {
1279 doEQU = 0;
1280 argstart = curLine+1;
1281 } else {
1282 doEQU = 1;
1283 argstart = strIsCommand("EQU", curLine);
1285 if (!argstart || doEQU) {
1286 if (pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1288 if (argstart) {
1289 // do '=' or 'EQU'
1290 memmove(curLine, argstart, strlen(argstart)+1);
1291 if (!doEQU) {
1292 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label '%s'", lbl->name);
1294 int defined = 1, addr = UR_FIXUP_NONE;
1295 int32_t res = getOneExprArg(&defined, &addr);
1296 lbl->type = doEQU;
1297 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1298 if (defined) {
1299 lbl->value = res;
1300 lbl->known = 1;
1301 } else {
1302 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1304 curLine[0] = '\0';
1305 return;
1307 // code label
1308 if (lbl->name[0] != '{' && !noLocAff) {
1309 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
1310 lastSeenGlobalLabel = strdup(lbl->name);
1312 lbl->type = 2;
1313 lbl->value = disp;
1314 lbl->known = 1;
1315 lbl->fixuptype = UR_FIXUP_WORD;
1319 ///////////////////////////////////////////////////////////////////////////////
1320 // instruction finder (in source)
1322 /* array ends with NULL */
1323 /* returns line or NULL */
1324 /* iidx will be set to found instruction number */
1325 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
1326 if (iidx) *iidx = -1;
1327 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
1328 va_list ap;
1329 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1330 va_start(ap, iidx);
1331 for (int f = 0; ;++f) {
1332 const char *name = va_arg(ap, const char *);
1334 if (!name) break;
1335 if (strIsCommand(name, cur->line)) {
1336 va_end(ap);
1337 if (iidx) *iidx = f;
1338 return cur;
1341 va_end(ap);
1343 return NULL;
1347 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
1348 return findNextInstructionFromList(NULL, name, NULL);
1352 ///////////////////////////////////////////////////////////////////////////////
1353 // writers
1355 static int optWriteFixups = 0;
1356 static int optFixupType = 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1357 static int optRunTape = 1;
1358 static int optRunDMB = 1;
1359 static int optRunSCL = 1;
1360 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1361 static int optSNA48 = 1;
1362 static char *optOutputDir = NULL;
1365 ///////////////////////////////////////////////////////////////////////////////
1366 static void writeFixups (void) {
1368 void writeFixupList (FILE *fo, int cnt, const char *lbl, int (*chk)(const FixupItem *)) {
1369 if (cnt > 0) {
1370 int prevaddr = 0;
1371 fprintf(fo, "%s:\n", lbl);
1372 if (optFixupType == 1) fprintf(fo, " dw %d ; count\n", cnt);
1373 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1374 if (chk(fx)) {
1375 fprintf(fo, " dw #%04X\n", fx->opaddr-prevaddr);
1376 if (optFixupType == 2) {
1377 prevaddr = fx->opaddr;
1381 if (optFixupType == 2 || optFixupType == 3) fprintf(fo, " dw 0 ; end marker\n");
1385 if (optFixupType == 0) {
1386 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.txt");
1387 FILE *fo = fopen(fname, "w");
1389 free(fname);
1390 if (fo == NULL) fatal("can't write fixup file");
1391 fprintf(fo, "; addr dadr sz\n");
1392 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1393 static const char type[4] = "NWLH";
1394 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
1396 fclose(fo);
1397 } else {
1398 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.zas");
1399 FILE *fo = fopen(fname, "w");
1400 int cntw = 0, cntwl = 0, cntwh = 0, cntbl = 0, cntbh = 0;
1401 free(fname);
1402 if (fo == NULL) fatal("can't write fixup file");
1403 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1404 if (fx->fixuptype == UR_FIXUP_WORD) { ++cntw; continue; }
1405 switch (fx->fixuptype) {
1406 case UR_FIXUP_LOBYTE: if (fx->size == 2) ++cntwl; else ++cntbl; break;
1407 case UR_FIXUP_HIBYTE: if (fx->size == 2) ++cntwh; else ++cntbh; break;
1410 writeFixupList(fo, cntw, "fixup_table_w", lambda(int, (const FixupItem *fx) {
1411 return (fx->fixuptype == UR_FIXUP_WORD);
1412 }));
1413 writeFixupList(fo, cntwl, "fixup_table_wl", lambda(int, (const FixupItem *fx) {
1414 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 2);
1415 }));
1416 writeFixupList(fo, cntwh, "fixup_table_wh", lambda(int, (const FixupItem *fx) {
1417 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 2);
1418 }));
1419 writeFixupList(fo, cntbl, "fixup_table_bl", lambda(int, (const FixupItem *fx) {
1420 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 1);
1421 }));
1422 writeFixupList(fo, cntbh, "fixup_table_bh", lambda(int, (const FixupItem *fx) {
1423 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 1);
1424 }));
1425 fclose(fo);
1430 ///////////////////////////////////////////////////////////////////////////////
1431 /* return 'found' flag */
1432 static int findChunkFrom (int addr, int *start, int *len) {
1433 if (addr < 0) addr = 0;
1434 for (; addr <= 65535 && !memused[addr]; ++addr) {}
1435 if (addr > 65535) return 0;
1436 *start = addr;
1437 for (; addr <= 65535 && memused[addr]; ++addr) {}
1438 *len = addr-(*start);
1439 return 1;
1443 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
1444 for (; buflen >= 2; buflen -= 2) {
1445 int cnt = *buf++;
1446 uint8_t byte = *buf++;
1448 if (cnt == 0) {
1449 // copy
1450 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
1451 if (!memused[addr]) putByte(addr, *buf);
1452 if (addr == 0xffff) return;
1454 } else {
1455 // fill
1456 for (; cnt > 0; --cnt, ++addr) {
1457 if (!memused[addr]) putByte(addr, byte);
1458 if (addr == 0xffff) return;
1465 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1466 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1467 if (bxor) *bxor = (*bxor)^b;
1468 return 0;
1472 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1473 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1474 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1475 return 0;
1479 ///////////////////////////////////////////////////////////////////////////////
1480 // .sna
1482 static int saveSna (const char *fname, int as48) {
1483 char *fn;// = malloc(strlen(fname)+16);
1484 uint8_t regs[27];
1485 FILE *fo;
1486 char abuf[32];
1488 fn = strprintf("%s/%s.sna", optOutputDir, fname);
1489 fo = fopen(fn, "wb");
1490 free(fn);
1491 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1492 printf("out: %s.sna\n", fname);
1493 memmove(regs, ursna48, 27);
1494 #if 0
1495 if (optRunSNA) {
1496 /* push new address */
1497 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1498 sp -= 2;
1499 putByte(sp, ent&0xFFU);
1500 putByte(sp+1, (ent>>8)&0xFFU);
1501 regs[23] = sp&0xFFU;
1502 regs[24] = (sp>>8)&0xFFU;
1504 fwrite(regs, 27, 1, fo);
1505 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1506 fwrite(memory+16384, 49152, 1, fo);
1507 #endif
1509 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1511 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1512 //fprintf(stderr, "%d\n", memused[sp]);
1513 if (memused[sp] || memused[(sp+1)&0xffff]) {
1514 fprintf(stderr, "FATAL: can't save snapshot!\n");
1515 goto error;
1518 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
1519 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
1520 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1522 sprintf(abuf, "%05d", clrAddr);
1523 memcpy(memory+23762, abuf, 5);
1524 sprintf(abuf, "%05d", ent);
1525 memcpy(memory+23773, abuf, 5);
1527 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
1529 if (!as48) {
1530 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
1532 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
1533 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
1534 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
1535 // write pages
1536 memset(memory, 0, 16384);
1537 for (int f = 1; f < 8; ++f) {
1538 if (f != 2 && f != 5) {
1539 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
1544 fclose(fo);
1545 return 0;
1546 error:
1547 fclose(fo);
1548 unlink(fname);
1549 return -1;
1553 ///////////////////////////////////////////////////////////////////////////////
1554 // bin chunks
1556 static void saveRaw (const char *basename) {
1557 char *fname = NULL;// = malloc(strlen(basename)+16);
1558 int start = 0, len;
1559 FILE *fo;
1561 while (findChunkFrom(start, &start, &len)) {
1562 if (fname != NULL) free(fname);
1563 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, (optTapExt ? "tap" : "bin"));
1564 fo = fopen(fname, "wb");
1565 if (!fo) {
1566 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1567 } else {
1568 printf("out: %s\n", fname);
1569 if (fwrite(memory+start, len, 1, fo) != 1) {
1570 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1571 fclose(fo);
1572 unlink(fname);
1573 } else {
1574 fclose(fo);
1577 start += len;
1579 if (fname != NULL) free(fname);
1583 ///////////////////////////////////////////////////////////////////////////////
1584 // HoBeta files
1586 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
1587 char name[8];
1588 char type;
1589 uint16_t start;
1590 uint16_t len;
1591 uint8_t zero; // always zero
1592 uint8_t secLen; // length in sectors
1593 uint16_t checksum;
1594 } HOBHeader;
1597 static uint16_t calcHobSum (const void *hdr) {
1598 const uint8_t *buf = (const uint8_t *)hdr;
1599 uint16_t res = 0;
1601 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
1602 return res;
1606 static void saveHob (const char *basename) {
1607 char *fname = NULL;//malloc(strlen(basename)+16);
1608 int start = 0, len;
1609 HOBHeader hdr;
1610 FILE *fo;
1612 while (findChunkFrom(start, &start, &len)) {
1613 if (fname != NULL) free(fname);
1614 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, "$C");
1615 fo = fopen(fname, "wb");
1616 if (!fo) {
1617 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1618 start += len;
1619 } else {
1620 char tmpbuf[sizeof(hdr.name)*2];
1621 printf("out: %s\n", fname);
1622 memset(&hdr, 0, sizeof(hdr));
1623 memset(&hdr.name, 32, 8);
1624 snprintf(tmpbuf, sizeof(tmpbuf), "%c%04X", basename[0], start);
1625 while (strlen(tmpbuf) < sizeof(hdr.name)) strcat(tmpbuf, " "); /* sorry! */
1626 memcpy(hdr.name, tmpbuf, sizeof(hdr.name));
1627 hdr.type = 'C';
1628 hdr.start = start;
1629 hdr.len = len;
1630 hdr.secLen = (len+255)/256;
1631 hdr.checksum = calcHobSum(&hdr);
1632 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
1633 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1634 fclose(fo);
1635 unlink(fname);
1636 goto quit;
1638 if (fwrite(memory+start, len, 1, fo) != 1) {
1639 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1640 fclose(fo);
1641 unlink(fname);
1642 goto quit;
1644 start += len;
1645 while (len%256) {
1646 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
1647 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1648 fclose(fo);
1649 unlink(fname);
1650 goto quit;
1652 ++len;
1653 //fprintf(stderr, ":%d\n", len%256);
1655 fclose(fo);
1658 quit:
1659 if (fname != NULL) free(fname);
1663 // ////////////////////////////////////////////////////////////////////////// //
1664 typedef struct {
1665 uint8_t fcount;
1666 uint8_t dir[14*256];
1667 uint32_t dirpos;
1668 uint32_t fpos;
1669 uint8_t *data;
1670 size_t datapos;
1671 size_t datasize;
1672 } SCLFile;
1675 static void sclInit (SCLFile *scl) {
1676 memset(scl, 0, sizeof(*scl));
1677 scl->datasize = 2*80*16*256; /* maximum disk size */
1678 scl->data = malloc(scl->datasize);
1679 memset(scl->data, 0, scl->datasize);
1680 scl->fpos = 0xffffffffu;
1684 static void sclFree (SCLFile *scl) {
1685 if (!scl) return;
1686 if (scl->data) free(scl->data);
1687 memset(scl, 0, sizeof(*scl));
1688 scl->fpos = 0xffffffffu;
1692 static void sclStartFile (SCLFile *scl, const char *name, char type, uint16_t v0, uint16_t v1) {
1693 if (scl->fcount == 255) {
1694 fprintf(stderr, "FATAL: too many files in SCL!\n");
1695 exit(1);
1697 if (scl->fpos != 0xffffffffu) {
1698 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1699 exit(1);
1701 int nlen = 0;
1702 while (nlen < 8 && name && name[nlen]) scl->dir[scl->dirpos++] = name[nlen++];
1703 while (nlen < 8) { scl->dir[scl->dirpos++] = ' '; ++nlen; }
1704 scl->dir[scl->dirpos++] = type;
1705 scl->dir[scl->dirpos++] = v0&0xff;
1706 scl->dir[scl->dirpos++] = (v0>>8)&0xff;
1707 scl->dir[scl->dirpos++] = v1&0xff;
1708 scl->dir[scl->dirpos++] = (v1>>8)&0xff;
1709 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1710 scl->fpos = scl->datapos;
1714 static void sclEndFile (SCLFile *scl) {
1715 if (scl->fpos == 0xffffffffu) {
1716 fprintf(stderr, "FATAL: last SCL file already finished!\n");
1717 exit(1);
1719 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
1720 const uint32_t fsz = scl->datapos-scl->fpos;
1721 if (fsz > 255*256) {
1722 fprintf(stderr, "FATAL: SCL file too big!\n");
1723 exit(1);
1725 scl->dir[scl->dirpos++] = ((fsz+255)/256)&0xff; /* size in sectors */
1726 ++scl->fcount;
1727 scl->fpos = 0xffffffffu;
1731 static void sclWriteData (SCLFile *scl, const void *buf, size_t len) {
1732 if (scl->fpos == 0xffffffffu) {
1733 fprintf(stderr, "FATAL: no open SCL file!\n");
1734 exit(1);
1736 if (!len) return;
1737 if (len > 255*256) {
1738 fprintf(stderr, "FATAL: SCL file too big!\n");
1739 exit(1);
1741 memcpy(scl->data+scl->datapos, buf, len);
1742 scl->datapos += len;
1746 static void sclWriteByte (SCLFile *scl, uint8_t b) {
1747 sclWriteData(scl, &b, 1);
1751 static void sclWriteWord (SCLFile *scl, uint16_t w) {
1752 uint8_t b = w&0xff;
1753 sclWriteData(scl, &b, 1);
1754 b = (w>>8)&0xff;
1755 sclWriteData(scl, &b, 1);
1759 #define SCL_DO_WRITE(buf_,size_) do { \
1760 if ((size_) == 0) break; \
1761 const uint8_t *p = (const uint8_t *)(buf_); \
1762 for (size_t n = (size_t)(size_); n--; ++p) checksum += *p; \
1763 if (fwrite((buf_), (size_t)(size_), 1, fo) != 1) return -1; \
1764 } while (0)
1766 // <0: error; 0: ok
1767 static int sclSaveToFile (FILE *fo, SCLFile *scl) {
1768 if (scl->fpos != 0xffffffffu) {
1769 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1770 exit(1);
1772 const char *sign = "SINCLAIR";
1773 uint32_t checksum = 0;
1774 // header
1775 SCL_DO_WRITE(sign, 8);
1776 SCL_DO_WRITE(&scl->fcount, 1);
1777 // directory
1778 SCL_DO_WRITE(scl->dir, scl->dirpos);
1779 // data
1780 SCL_DO_WRITE(scl->data, scl->datapos);
1781 // write checksum
1782 for (unsigned f = 0; f < 4; ++f) {
1783 const uint8_t b = (checksum>>(f*8))&0xff;
1784 SCL_DO_WRITE(&b, 1);
1786 // done
1787 return 0;
1791 // ////////////////////////////////////////////////////////////////////////// //
1792 static void saveSCLCargador (SCLFile *scl) {
1793 static uint8_t cargador[16384]; // should be enough for everyone
1794 int linestart = -1;
1795 int linenum = 0;
1796 int start = 0, len, pos;
1798 void putStr (const char *s) {
1799 for (; *s; ++s) cargador[pos++] = *s;
1802 void putNum (int num) {
1803 char buf[64];
1804 sprintf(buf, "%d", num);
1805 putStr(buf);
1808 void startLine () {
1809 ++linenum;
1810 linestart = pos;
1811 // number
1812 cargador[pos++] = (linenum>>8)&0xff;
1813 cargador[pos++] = linenum&0xff;
1814 // size (will be fixed later)
1815 cargador[pos++] = 0;
1816 cargador[pos++] = 0;
1819 void flushLine () {
1820 if (linestart >= 0) {
1821 const int size = pos-linestart-4;
1822 cargador[linestart+2] = size&0xff;
1823 cargador[linestart+3] = (size>>8)&0xff;
1825 linestart = -1;
1828 char cname[16];
1830 pos = 0;
1831 startLine();
1832 // CLEAR VAL "xxx"
1833 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
1834 // load blocks
1835 int cont = 1;
1836 while (findChunkFrom(start, &start, &len)) {
1837 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1838 if (cont) { putStr(":"); cont = 0; }
1839 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1840 // generate chunk name
1841 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
1842 putStr(cname);
1843 //" CODE
1844 putStr("\"\xaf\r");
1845 flushLine();
1846 startLine();
1847 start += len;
1849 // and run
1850 if (cont) { putStr(":"); cont = 0; }
1851 // RANDOMIZE USR VAL "xxx"
1852 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"");
1853 putStr("\r");
1854 flushLine();
1856 //putWord(0xaa80);
1857 //putWord(1); // autostart line
1859 // write to SCL
1860 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
1861 sclWriteData(scl, cargador, (size_t)pos);
1862 sclWriteByte(scl, 0x80);
1863 sclWriteByte(scl, 0xaa);
1864 sclWriteWord(scl, 1);
1865 sclEndFile(scl);
1869 static void saveSCL (const char *basename) {
1870 SCLFile scl;
1871 int fcount = 0;
1872 int start, len;
1873 char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
1874 // count files
1875 start = 0;
1876 while (findChunkFrom(start, &start, &len)) {
1877 ++fcount;
1878 start += len;
1880 if (fcount && optRunSCL) fcount += 2; // +loader and boot
1881 if (fcount > 255) {
1882 fprintf(stderr, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname, fcount);
1883 exit(1);
1885 // create output file
1886 FILE *fo = fopen(fname, "wb");
1887 if (!fo) {
1888 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
1889 exit(1);
1891 // initialise SCL writer
1892 sclInit(&scl);
1893 // write loader
1894 if (fcount && optRunSCL) {
1895 // create simple boot
1896 const uint8_t dasboot[] = {
1897 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"
1899 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
1900 sclWriteWord(&scl, 1); // line number
1901 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
1902 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
1903 // TR-DOS signature
1904 sclWriteByte(&scl, 0x80);
1905 sclWriteByte(&scl, 0xaa);
1906 // start line
1907 sclWriteWord(&scl, 0);
1908 sclEndFile(&scl);
1909 saveSCLCargador(&scl);
1911 // write chunks
1912 char cname[16];
1913 start = 0;
1914 while (findChunkFrom(start, &start, &len)) {
1915 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
1916 sclStartFile(&scl, cname, 'C', (unsigned)start, (unsigned)len);
1917 sclWriteData(&scl, memory+(unsigned)start, (unsigned)len);
1918 sclEndFile(&scl);
1919 start += len;
1921 if (sclSaveToFile(fo, &scl) < 0) {
1922 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
1923 fclose(fo);
1924 unlink(fname);
1925 exit(1);
1927 sclFree(&scl);
1928 fclose(fo);
1929 printf("out: %s\n", fname);
1930 if (fname != NULL) free(fname);
1934 ///////////////////////////////////////////////////////////////////////////////
1935 // .dmb
1937 static int saveDMB (const char *fname) {
1938 char *fn;// = malloc(strlen(fname)+16);
1939 int start = 0, len;
1940 uint16_t pcnt = 0;
1941 FILE *fo;
1943 fn = strprintf("%s/%s.dmb", optOutputDir, fname);
1944 fo = fopen(fn, "wb");
1945 free(fn);
1946 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
1948 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
1950 printf("out: %s.dmb\n", fname);
1951 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
1952 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
1953 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
1954 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
1956 start = 0;
1957 while (findChunkFrom(start, &start, &len)) {
1958 if (fWriteWord(fo, len, NULL) != 0) goto error;
1959 if (fWriteWord(fo, start, NULL) != 0) goto error;
1960 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
1961 start += len;
1963 fclose(fo);
1964 return 0;
1965 error:
1966 fclose(fo);
1967 unlink(fname);
1968 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
1969 return -1;
1973 ///////////////////////////////////////////////////////////////////////////////
1974 // .tap
1976 static char tapeLoaderName[16];
1979 static void saveTapCargador (FILE *fo) {
1980 // count blocks
1981 static uint8_t cargador[16384]; // should be enough for everyone
1982 int start = 0, len, pos, f;
1983 uint8_t bxor;
1986 void putStr (const char *s) {
1987 for (; *s; ++s) cargador[pos++] = *s;
1990 void putNum (int num) {
1991 char buf[64];
1992 sprintf(buf, "%d", num);
1993 putStr(buf);
1997 pos = 4;
1998 // number
1999 cargador[0] = 0; cargador[1] = 10;
2000 // size (will be fixed later)
2001 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
2002 // load blocks
2003 while (findChunkFrom(start, &start, &len)) {
2004 // :LOAD "" CODE
2005 putStr(":\xef\"\"\xaf");
2006 start += len;
2008 // and run
2009 // :RANDOMIZE USR VAL "xxx"
2010 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
2011 // patch len
2012 cargador[2] = (pos-4)&0xff;
2013 cargador[3] = ((pos-4)>>8)&0xff;
2014 // write header
2015 fWriteWord(fo, 19, NULL); // length of header
2016 bxor = 0;
2017 fWriteByte(fo, 0, &bxor); // header block
2018 fWriteByte(fo, 0, &bxor); // 'basic' flag
2019 if (tapeLoaderName[0]) {
2020 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2021 } else {
2022 fWriteByte(fo, 'c', &bxor);
2023 fWriteByte(fo, 'a', &bxor);
2024 fWriteByte(fo, 'r', &bxor);
2025 fWriteByte(fo, 'g', &bxor);
2026 fWriteByte(fo, 'a', &bxor);
2027 fWriteByte(fo, 'd', &bxor);
2028 fWriteByte(fo, 'o', &bxor);
2029 fWriteByte(fo, 'r', &bxor);
2030 fWriteByte(fo, ' ', &bxor);
2031 fWriteByte(fo, ' ', &bxor);
2033 fWriteWord(fo, pos, &bxor); // length
2034 fWriteWord(fo, 10, &bxor); // start
2035 fWriteWord(fo, pos, &bxor); // length2
2036 fWriteByte(fo, bxor, NULL);
2037 // write data
2038 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
2039 bxor = 0;
2040 fWriteByte(fo, 0xFFU, &bxor); // data block
2041 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
2042 fWriteByte(fo, bxor, NULL);
2046 static void saveTap (const char *basename) {
2047 char *fname;// = malloc(strlen(basename)+16);
2048 char blkname[128];
2049 int start = 0, len, f;
2050 uint8_t bxor;
2051 FILE *fo;
2053 fname = strprintf("%s/%s.tap", optOutputDir, basename);
2054 fo = fopen(fname, "wb");
2055 free(fname);
2056 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
2057 printf("out: %s.tap\n", basename);
2058 if (optRunTape) saveTapCargador(fo);
2059 while (findChunkFrom(start, &start, &len)) {
2060 // write header
2061 if (tapeLoaderName[0]) {
2062 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2063 memcpy(blkname, tapeLoaderName, 10);
2064 } else {
2065 sprintf(blkname, "c%04X:%04X", start, len);
2067 //printf(" block: %s\n", blkname);
2068 fWriteWord(fo, 19, NULL); // length of header
2069 bxor = 0;
2070 fWriteByte(fo, 0, &bxor); // header block
2071 fWriteByte(fo, 3, &bxor); // 'code' flag
2072 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
2073 fWriteWord(fo, len, &bxor);
2074 fWriteWord(fo, start, &bxor);
2075 fWriteWord(fo, 32768, &bxor);
2076 fWriteByte(fo, bxor, NULL);
2077 // write data
2078 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
2079 bxor = 0;
2080 fWriteByte(fo, 0xFFU, &bxor); // data block
2081 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
2082 fWriteByte(fo, bxor, NULL);
2083 start += len;
2085 fclose(fo);
2089 ///////////////////////////////////////////////////////////////////////////////
2090 // pseudoinstructions
2092 // note that processCurrentLine() will NOT skip to the next line before
2093 // calling pseudoinstruction handler!
2094 // note that processCurrentLine() will skip current line after calling
2095 // pseudoinstruction handler!
2097 static int wasOrg = 0;
2098 static int wasClr = 0;
2101 // ////////////////////////////////////////////////////////////////////////// //
2102 // print message using printf-like syntax
2103 // doesn't print new line
2104 static void processPrintf (FILE *fo, const char *fmt) {
2105 if (!fmt || !fmt[0]) return;
2106 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2107 char *tempstr = NULL;
2108 size_t tempsize = 0;
2109 char *fmtcopy = malloc(strlen(fmt)+1);
2110 strcpy(fmtcopy, fmt);
2111 char *currfmt = fmtcopy;
2112 char tempbuf[512];
2113 int stlen;
2114 char *strarg;
2115 int defined;
2116 int32_t exprval;
2117 while (*currfmt) {
2118 /* find '%' */
2119 char *prcs = strchr(currfmt, '%');
2120 if (!prcs || !prcs[1]) {
2121 /* no more formatting; print the tail and exit the loop */
2122 fprintf(fo, "%s", currfmt);
2123 break;
2125 /* is this "%%"? */
2126 int docheck = 1;
2127 if (prcs[1] == '%') {
2128 ++prcs;
2129 docheck = 0;
2131 /* print up to `prcs` */
2132 if (prcs > currfmt) {
2133 size_t partlen = (ptrdiff_t)(prcs-currfmt);
2134 if (partlen+1 > tempsize) {
2135 tempsize = ((partlen+8)|0xff)+1;
2136 tempstr = realloc(tempstr, tempsize);
2138 memcpy(tempstr, currfmt, partlen);
2139 tempstr[partlen] = 0;
2140 fprintf(fo, "%s", tempstr);
2142 currfmt = ++prcs; /* skip percent */
2143 if (!docheck) continue;
2144 /* parse format */
2145 char sign = ' ';
2146 int width = 0;
2147 int zerofill = 0;
2148 char ftype = ' ';
2149 if (*currfmt == '+' || *currfmt == '-') sign = *currfmt++;
2150 if (sign != '-' && *currfmt == '0') zerofill = 1;
2151 while (isDigit(*currfmt)) { width = width*10+((*currfmt)-'0'); ++currfmt; }
2152 if (width > 256) width = 256;
2153 ftype = *currfmt++;
2154 if (!ftype) break; /* oops */
2155 if (!eatComma()) fatal("out of arguments for string format");
2156 switch (ftype) {
2157 case 's': /* string */
2158 stlen = 0;
2159 switch (strSkipSpaces(curLine)[0]) {
2160 case '"': case '\'': /* string literal? */
2161 strarg = getStrArg(&stlen);
2162 break;
2163 default: /* expression */
2164 strarg = getStrExprArg();
2165 stlen = (int)strlen(strarg);
2166 break;
2168 /* left pad */
2169 if (sign != '-' && stlen < width) {
2170 int padlen = width-stlen;
2171 memset(tempbuf, ' ', padlen);
2172 tempbuf[padlen+1] = 0;
2173 fprintf(fo, "%s", tempbuf);
2175 fprintf(fo, "%s", strarg);
2176 /* right pad */
2177 if (sign == '-' && stlen < width) {
2178 int padlen = width-stlen;
2179 memset(tempbuf, ' ', padlen);
2180 tempbuf[padlen+1] = 0;
2181 fprintf(fo, "%s", tempbuf);
2183 break;
2184 case 'd': /* decimal */
2185 defined = 1;
2186 exprval = getExprArg(&defined, NULL);
2187 if (width && zerofill) {
2188 fprintf(fo, "%0*d", width, exprval);
2189 } else if (width) {
2190 fprintf(fo, "%*d", (sign != '-' ? width : -width), exprval);
2191 } else {
2192 fprintf(fo, "%d", exprval);
2194 break;
2195 case 'c': /* char */
2196 defined = 1;
2197 exprval = getExprArg(&defined, NULL);
2198 if (exprval <= 0 || exprval == '?' || exprval > 255) exprval = '?';
2199 if (width) {
2200 fprintf(fo, "%*c", (sign != '-' ? width : -width), exprval);
2201 } else {
2202 fprintf(fo, "%c", exprval);
2204 break;
2205 case 'u': /* unsigned */
2206 defined = 1;
2207 exprval = getExprArg(&defined, NULL);
2208 if (width && zerofill) {
2209 fprintf(fo, "%0*u", width, (unsigned)(exprval&0xffff));
2210 } else if (width) {
2211 fprintf(fo, "%*u", (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2212 } else {
2213 fprintf(fo, "%u", (unsigned)(exprval&0xffff));
2215 break;
2216 case 'x': case 'X': /* hex */
2217 defined = 1;
2218 exprval = getExprArg(&defined, NULL);
2219 if (width && zerofill) {
2220 fprintf(fo, (ftype == 'x' ? "%0*x" : "%0*X"), width, (unsigned)(exprval&0xffff));
2221 } else if (width) {
2222 fprintf(fo, (ftype == 'x' ? "%*x" : "%*X"), (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2223 } else {
2224 fprintf(fo, (ftype == 'x' ? "%x" : "%X"), (unsigned)(exprval&0xffff));
2226 break;
2227 default:
2228 if (ftype <= 0 || ftype == 127) ftype = '?';
2229 fatal("invalid format specifier: '%c'", ftype);
2230 break;
2233 if (tempstr) free(tempstr);
2234 free(fmtcopy);
2238 ///////////////////////////////////////////////////////////////////////////////
2239 // user error
2241 static int piERROR (void) {
2242 int len = 0;
2243 char *res = getStrArg(&len);
2244 fprintf(stdout, "*** USER ERROR: ");
2245 processPrintf(stdout, res);
2246 fputc('\n', stdout);
2247 fatal("user error abort");
2248 return PI_SKIP_LINE;
2252 static int piWARNING (void) {
2253 int len = 0;
2254 char *res = getStrArg(&len);
2255 fprintf(stdout, "*** USER WARNING ");
2256 if (curSrcLine) {
2257 fprintf(stdout, "at file %s, line %d: ", curSrcLine->fname, curSrcLine->lineNo);
2258 } else {
2259 fprintf(stdout, "somewhere in time: ");
2261 processPrintf(stdout, res);
2262 fputc('\n', stdout);
2263 return PI_SKIP_LINE;
2267 ///////////////////////////////////////////////////////////////////////////////
2268 // user warnings
2270 static int piPrintfCommon (int passNo) {
2271 if (passNo < 0 || pass == passNo) {
2272 int len = 0;
2273 char *res = getStrArg(&len);
2274 processPrintf(stdout, res);
2275 fputc('\n', stdout);
2277 return PI_SKIP_LINE;
2281 static int piDISPLAYX (int passNo, int asHex) {
2282 for (;;) {
2283 if (isStrArg()) {
2284 int len = 0;
2285 char *res = getStrArg(&len);
2287 if (passNo < 0 || pass == passNo) printf("%s", res);
2288 } else {
2289 int defined = 1;
2290 int32_t v = getExprArg(&defined, NULL);
2292 if (passNo < 0 || pass == passNo) {
2293 if (asHex) printf("%04X", (unsigned int)v);
2294 else printf("%d", v);
2297 if (!eatComma()) break;
2299 return PI_SKIP_LINE;
2303 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2304 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2305 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2306 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2307 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2308 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2310 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2311 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2312 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2315 ///////////////////////////////////////////////////////////////////////////////
2316 // ORG, DISP, etc.
2318 static int piORG (void) {
2319 int defined = 1;
2320 int32_t res = getOneExprArg(&defined, NULL);
2322 if (!defined) fatal("sorry, ORG operand value must be known here");
2323 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
2324 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
2325 pc = disp = res;
2326 if (!wasOrg) {
2327 wasOrg = 1;
2328 ent = res;
2329 if (!wasClr && res > 0) clrAddr = res-1;
2331 return PI_CONT_LINE;
2335 static int piDISP (void) {
2336 int defined = 1;
2337 int32_t res = getOneExprArg(&defined, NULL);
2339 if (!defined) fatal("sorry, DISP operand value must be known here");
2340 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
2341 //printf("DISP=%d\n", res);
2342 disp = res;
2343 return PI_CONT_LINE;
2347 static int piENDDISP (void) {
2348 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2349 checkExprEnd();
2350 disp = pc;
2351 return PI_CONT_LINE;
2355 static int piENT (void) {
2356 int defined = 1;
2357 int32_t res = getOneExprArg(&defined, NULL);
2359 //if (!defined) fatal("sorry, ENT operand value must be known here");
2360 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
2361 ent = res;
2362 return PI_CONT_LINE;
2366 static int piCLR (void) {
2367 int defined = 1;
2368 int32_t res = getOneExprArg(&defined, NULL);
2370 //if (!defined) fatal("sorry, CLR operand value must be known here");
2371 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
2372 clrAddr = res;
2373 wasClr = 1;
2374 return PI_CONT_LINE;
2378 static int piRESERVE (void) {
2380 int defined = 1, start;
2381 int32_t res = getExprArg(&defined, NULL);
2382 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2383 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2384 start = res;
2385 if (!eatComma()) fatal("RESERVE needs 2 args");
2386 res = getOneExprArg(&defined, NULL);
2387 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2388 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2389 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2391 fatal("RESERVE: not yet!");
2392 return PI_CONT_LINE;
2396 static int piALIGN (void) {
2397 int defined = 1;
2398 int32_t res;
2400 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2401 res = getOneExprArg(&defined, NULL);
2402 if (!defined) fatal("sorry, ALIGN operand value must be known here");
2403 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
2404 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
2405 if (res > 0 && pc%res != 0) {
2406 pc /= res;
2407 pc *= res;
2408 pc += res;
2409 disp = pc;
2411 return PI_CONT_LINE;
2415 static int piDISPALIGN (void) {
2416 int defined = 1;
2417 int32_t res = getOneExprArg(&defined, NULL);
2419 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
2420 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
2421 if (res > 0 && disp%res != 0) {
2422 disp /= res;
2423 disp *= res;
2424 disp += res;
2426 return PI_CONT_LINE;
2430 ///////////////////////////////////////////////////////////////////////////////
2431 // DEFx
2433 static int defIncr = 0;
2436 static int piDEFINCR (void) {
2437 int defined = 1;
2438 int32_t cnt = getOneExprArg(&defined, NULL);
2440 if (!defined) fatal("DEFINCR: increment must be defined");
2441 defIncr = cnt;
2442 return PI_CONT_LINE;
2446 static int piDEFBW (int isWord) {
2447 for (;;) {
2448 int defined = 0, fixuptype = UR_FIXUP_NONE;
2450 if (isStrArg()) {
2451 int f, len = 0;
2452 char *res = getStrArg(&len);
2454 for (f = 0; f < len; ++f) {
2455 int32_t b = (uint8_t)res[f];
2456 b += defIncr;
2457 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
2458 if (b < 0) b += 256;
2459 emitByte(b);
2461 } else {
2462 int32_t res = getExprArg(&defined, &fixuptype);
2464 if (pass > 0 && !defined) fatal("undefined operand");
2465 res += defIncr;
2466 if (isWord) {
2467 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
2468 if (res < 0) res += 65536;
2469 if (isWord == 1) {
2470 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 2);
2471 emitWord(res);
2472 } else {
2473 /* reversed word */
2474 switch (fixuptype) {
2475 case UR_FIXUP_WORD: /* swapped bytes */
2476 urasm_fixup_operand(NULL, pc, disp, UR_FIXUP_HIBYTE, 1);
2477 urasm_fixup_operand(NULL, pc, disp+1, UR_FIXUP_LOBYTE, 1);
2478 break;
2479 case UR_FIXUP_LOBYTE:
2480 case UR_FIXUP_HIBYTE:
2481 warningMsg("non-word fixup for reversed word; wtf?!");
2482 break;
2483 default: break;
2485 emitRWord(res);
2487 } else {
2488 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
2489 if (fixuptype != UR_FIXUP_NONE) {
2490 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2491 urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2493 if (res < 0) res += 256;
2494 emitByte(res);
2497 if (!eatComma()) break;
2499 return PI_CONT_LINE;
2502 static int piDEFB (void) { return piDEFBW(0); }
2503 static int piDEFW (void) { return piDEFBW(1); }
2504 static int piDEFR (void) { return piDEFBW(2); }
2507 static int piDEFS (void) {
2508 for (;;) {
2509 int32_t bt, f;
2510 int defined = 0, fixuptype = UR_FIXUP_NONE;
2511 int32_t res = getExprArg(&defined, &fixuptype);
2513 if (pass > 0 && !defined) fatal("undefined operand");
2514 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
2515 if (*curLine && curLine[0] == ',') {
2516 (void)eatComma();
2517 bt = getExprArg(&defined, NULL);
2518 if (pass > 0 && !defined) fatal("undefined operand");
2519 bt += defIncr;
2520 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
2521 if (bt < 0) bt += 256;
2522 if (fixuptype != UR_FIXUP_NONE) {
2523 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2525 for (f = 0; f < res; ++f) {
2526 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2527 emitByte(bt);
2529 } else {
2530 pc += res; disp += res;
2532 if (!eatComma()) break;
2534 return PI_CONT_LINE;
2538 /* bit 0: put '\0' */
2539 /* bit 1: set bit 7 of last byte */
2540 /* bit 2: put length */
2541 static int piDEFSTR (int type) {
2542 for (;;) {
2543 if (isStrArg()) {
2544 int f, len = 0;
2545 char *res = getStrArg(&len);
2547 if (type&0x04) {
2548 if (len > 255) fatal("string too long");
2549 emitByte(len);
2551 for (f = 0; f < len; ++f) {
2552 uint8_t b = (uint8_t)res[f];
2554 if ((type&0x02) && f == len-1) b |= 0x80;
2555 emitByte(b);
2557 if (type&0x01) emitByte(0);
2558 } else {
2559 int defined = 1;
2560 int32_t v = getExprArg(&defined, NULL);
2562 if (pass > 0 && !defined) fatal("undefined expression");
2563 if (!defined) v = 0; else v += defIncr;
2564 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
2565 if (v < 0) v += 256;
2566 emitByte(v);
2568 if (!eatComma()) break;
2570 return PI_CONT_LINE;
2574 static int piDEFM (void) { return piDEFSTR(0x00); }
2575 static int piDEFZ (void) { return piDEFSTR(0x01); }
2576 static int piDEFX (void) { return piDEFSTR(0x02); }
2577 static int piDEFC (void) { return piDEFSTR(0x04); }
2580 ///////////////////////////////////////////////////////////////////////////////
2581 // INCBIN
2583 /* INCBIN "name"[,maxlen] */
2584 static int piINCBIN (void) {
2585 int system = 0;
2586 char *fn, qCh;
2587 uint8_t bt;
2588 FILE *fl;
2589 int maxlen = 65536;
2590 char *args = curLine;
2592 if (!curLine[0]) fatal("INCBIN without file name");
2593 if (isStrArg()) {
2594 qCh = *args++;
2595 system = 0;
2596 } else if (curLine[0] == '<') {
2597 qCh = '>'; ++args;
2598 system = 1;
2599 } else {
2600 qCh = 0;
2601 system = 0;
2603 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2604 if (!fn[0]) fatal("INCBIN: empty file name");
2605 memmove(curLine, args, strlen(args)+1);
2606 // maxlen
2607 if (curLine[0] == ',') {
2608 int defined = 1;
2609 (void)eatComma();
2610 maxlen = getOneExprArg(&defined, NULL);
2611 if (!defined) fatal("INCBIN: undefined maxlen");
2612 if (maxlen < 1) return 1; // nothing to do
2614 // now fix name
2615 if (system) {
2616 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
2617 } else {
2618 sprintf(curLine, "%s", fn);
2621 fl = fopen(curLine, "rb");
2622 if (!fl) fatal("INCBIN: file not found: %s", curLine);
2623 while (maxlen-- > 0) {
2624 int res = fread(&bt, 1, 1, fl);
2626 if (!res) break;
2627 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: %s", curLine); }
2628 emitByte(bt);
2630 fclose(fl);
2631 return PI_SKIP_LINE;
2635 ///////////////////////////////////////////////////////////////////////////////
2636 // INCLUDE
2638 /* INCLUDE "name" */
2639 static int piINCLUDE (void) {
2640 int system = 0;
2641 char *fn, qCh;
2642 char *args = curLine;
2644 if (!curLine[0]) fatal("INCLUDE without file name");
2645 if (isStrArg()) {
2646 qCh = *args++;
2647 system = 0;
2648 } else if (curLine[0] == '<') {
2649 qCh = '>'; ++args;
2650 system = 1;
2651 } else {
2652 qCh = 0;
2653 system = 0;
2655 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2656 if (!fn[0]) fatal("INCLUDE: empty file name");
2658 if (asmTextInclude(fn, system) != 0) fatal("INCLUDE: some shit happens!");
2659 return PI_SKIP_LINE;
2663 ///////////////////////////////////////////////////////////////////////////////
2664 // MODULE, ENDMODULE
2666 static int piENDMODULE (void) {
2667 if (!curModule) fatal("ENDMODULE without MODULE");
2668 if (curLine[0]) {
2669 char *mn = getOneLabelArg();
2670 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
2672 curModule = NULL;
2673 return PI_SKIP_LINE;
2677 static int piMODULE (void) {
2678 ModuleInfo *mi;
2679 char *mn;
2680 SourceLine *ol = curSrcLine;
2681 int inum;
2683 if (curModule) fatal("no nested modules allowed");
2684 mn = getOneLabelArg();
2685 if (!urasm_is_valid_name(mn)) fatal("invalid module name: %s", mn);
2686 mi = moduleFind(mn);
2687 //printf("[%s] %p\n", mn, mi);
2688 if (mi) {
2689 if (mi->seen) {
2690 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
2691 /* skip module */
2692 nextSrcLine(); /* skip "MODULE" line */
2693 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
2694 setCurSrcLine(ol);
2695 fatal("no ENDMODULE");
2697 if (inum == 0) fatal("no nested modules allowed");
2698 curModule = mi;
2699 //skipInstruction(); //k8:wtf?!
2700 return piENDMODULE();
2702 } else {
2703 mi = moduleAdd(mn, curSrcLine->fname);
2705 mi->seen = 1;
2706 curModule = mi;
2707 return PI_SKIP_LINE;
2711 /* Z80, Z80N */
2712 static int piMODEL (void) {
2713 char *mn = getOneIdArgLo();
2714 if (strSkipSpaces(curLine)[0]) fatal("only one model name expected");
2715 if (strcmp(mn, "z80") == 0) urasm_allow_zxnext = 0;
2716 else if (strcmp(mn, "z80a") == 0) urasm_allow_zxnext = 0;
2717 else if (strcmp(mn, "z80n") == 0) urasm_allow_zxnext = 1;
2718 else if (strcmp(mn, "z80next") == 0) urasm_allow_zxnext = 1;
2719 else if (strcmp(mn, "zxnext") == 0) urasm_allow_zxnext = 1;
2720 else fatal("invalid model name: %s", mn);
2721 return PI_SKIP_LINE;
2725 ///////////////////////////////////////////////////////////////////////////////
2726 // DUP, EDUP
2728 static int piEDUP (void) {
2729 fatal("EDUP without DUP");
2730 checkOperatorEnd();
2731 return PI_SKIP_LINE;
2735 static int piDUP (void) {
2736 int defined = 1, dupCnt = 1, inum;
2737 SourceLine *stline, *eline = NULL;
2738 int32_t cnt = getOneExprArg(&defined, NULL);
2740 if (!defined) fatal("DUP: counter must be defined");
2741 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
2742 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
2743 // now find corresponding EDUP
2744 // note that we should skip nested DUPs
2745 nextSrcLine(); // skip ourself
2746 stline = curSrcLine;
2747 while (curSrcLine) {
2748 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
2749 // ok, we found something; what is it?
2750 if (inum == 0) {
2751 // new DUP, skip it
2752 ++dupCnt;
2753 nextSrcLine(); // skip DUP
2754 } else {
2755 // EDUP
2756 if (--dupCnt == 0) {
2757 // gotcha!
2758 eline = curSrcLine;
2759 break;
2761 nextSrcLine(); // skip EDUP
2764 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
2765 // now repeat that lines
2766 while (cnt-- > 0) {
2767 setCurSrcLine(stline);
2768 while (curSrcLine != eline) processCurrentLine();
2770 return PI_SKIP_LINE;
2774 ///////////////////////////////////////////////////////////////////////////////
2775 // IF, ENDIF
2777 static int ifCount = 0;
2780 // results:
2781 // -1: error (should not happen)
2782 // 0: successfully complete
2783 // 1: stopped *AT* "ELSE"
2784 // 2: stopped *AT* "ELSEIF"
2785 static int ifSkipToEndIfOrElse (int wholeBody) {
2786 int inum, wasElse = 0;
2787 SourceLine *oline;
2789 while (curSrcLine) {
2790 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
2791 switch (inum) {
2792 case 0: /* if */
2793 case 6: /* ifx */
2794 nextSrcLine(); // skip IF
2795 ifSkipToEndIfOrElse(1); // and recurse
2796 nextSrcLine(); // skip ENDIF
2797 break;
2798 case 1: /* else */
2799 if (wasElse) fatal("duplicate ELSE");
2800 if (!wholeBody) return 1;
2801 wasElse = 1;
2802 nextSrcLine(); // skip ELSE
2803 break; // and continue
2804 case 2: /* endif */
2805 return 0; // and exit
2806 case 3: /* elif */
2807 case 7: /* elifx */
2808 if (wasElse) fatal("ELSEIF in ELSE");
2809 if (!wholeBody) return 2;
2810 nextSrcLine(); // skip ELSEIF
2811 break; // and continue
2812 case 4: /* macro */
2813 // skip it as a whole
2814 nextSrcLine(); // skip MACRO
2815 for (;;) {
2816 oline = curSrcLine;
2817 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
2818 if (inum == 1) break;
2819 fatal("invalid nested MACRO");
2821 nextSrcLine(); // skip ENDM
2822 break;
2823 case 5: /* endm */
2824 fatal("unexpected ENDM");
2827 fatal("IF without ENDIF");
2828 return -1;
2832 static int piENDIF (void) {
2833 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2834 if (--ifCount < 0) fatal("ENDIF without IF");
2835 checkOperatorEnd();
2836 return PI_SKIP_LINE;
2840 static int piELSE (void) {
2841 if (--ifCount < 0) fatal("ELSE without IF");
2842 nextSrcLine(); // skip ELSE
2843 ifSkipToEndIfOrElse(1);
2844 return PI_SKIP_LINE;
2847 static int piELSEIF (void) {
2848 if (--ifCount < 0) fatal("ELSEIF without IF");
2849 nextSrcLine(); // skip ELSEIF
2850 ifSkipToEndIfOrElse(1);
2851 return PI_SKIP_LINE;
2855 static int piELSEIFX (void) {
2856 if (--ifCount < 0) fatal("ELSEIFX without IF");
2857 nextSrcLine(); // skip ELSEIFX
2858 ifSkipToEndIfOrElse(1);
2859 return PI_SKIP_LINE;
2863 static int piIFAll (int isIfX) {
2864 int defined = 1;
2865 int ooo = lblOptMakeU2;
2866 lblOptMakeU2 = (isIfX ? 1 : 0);
2867 int32_t cond = getOneExprArg(&defined, NULL);
2868 lblOptMakeU2 = ooo;
2870 if (!defined) {
2871 if (!isIfX) fatal("IF: condition must be defined");
2872 cond = 0; // for IFX: 0 if there is any undefined label
2874 if (cond) {
2875 // ok, do it until ELSE/ELSEIF/ENDIF
2876 ++ifCount;
2877 return PI_SKIP_LINE;
2879 for (;;) {
2880 int r;
2881 char *args;
2883 nextSrcLine(); // skip last instruction
2884 // skip until ELSE/ELSEIF/ENDIF
2885 r = ifSkipToEndIfOrElse(0);
2886 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2887 if (r == 0) break; // ENDIF
2888 if (r == 1) { ++ifCount; break; } // ELSE
2889 // ELSEIF, do condition
2890 args = strIsCommand("ELSEIF", curLine);
2891 if (args) {
2892 isIfX = 0;
2893 } else {
2894 if ((args = strIsCommand("ELSEIFX", curLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
2895 isIfX = 1;
2897 memmove(curLine, args, strlen(args)+1);
2898 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2899 cond = getOneExprArg(&defined, NULL);
2900 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
2901 if (!defined) {
2902 if (!isIfX) fatal("ELSEIF: condition must be defined");
2903 cond = 0; // for IFX: 0 if there is any undefined label
2905 if (cond) { ++ifCount; break; } // condition is true
2907 return PI_SKIP_LINE;
2911 static int piIF (void) { return piIFAll(0); }
2912 static int piIFX (void) { return piIFAll(1); }
2915 ///////////////////////////////////////////////////////////////////////////////
2916 // macro processor
2917 ///////////////////////////////////////////////////////////////////////////////
2919 what i did with MACRO is the brain-damaged cheating all the way.
2921 first, i will collect the MACRO body and remember it (removing it
2922 from the main source code, so second pass will not see it).
2924 second, when the macro is used, i will:
2925 * insert the whole macro body in place (label resolver will
2926 fix "..lbl" labels for us)
2927 * let the asm play with it
2929 this is not the best scheme, but it is fairly simple and it works.
2932 // will be called when parser encounters term starting with '=' or '*'
2933 // first term char will not be skipped
2934 // must return pointer to the first char after expression end
2935 static const char *getValueCB (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2936 char name[257];
2937 char *p = name;
2939 if (curmacro == NULL) fatal("'=' outside of macro");
2940 if (*expr++ != '=') fatal("'=' expected!");
2942 expr = strSkipSpaces(expr);
2943 while (*expr) {
2944 if (isAlphaDigit(*expr) || *expr == '_') {
2945 if (p-name > 250) fatal("id too long");
2946 *p++ = *expr++;
2947 continue;
2949 break;
2951 *p = 0;
2953 expr = strSkipSpaces(expr);
2954 for (int f = 0; f < curmacro->mac->argc; ++f) {
2955 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
2956 if (*expr == '[') {
2957 urasm_exprval_t v;
2958 int l = (int)strlen(curmacro->argvals[f]);
2959 ++expr; // skip "["
2960 urasm_exprval_init(&v);
2961 expr = urasm_expr_ex(&v, expr, addr, &donteval, defined, error);
2962 if (*error) return expr;
2963 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
2964 ++expr;
2965 if (v.val < 0) v.val += l;
2966 if (v.val < 0 || v.val >= l) {
2967 res->val = '?';
2968 } else {
2969 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
2971 return expr;
2972 } else {
2973 urasm_expr_ex(res, curmacro->argvals[f], addr, &donteval, defined, error);
2974 return expr;
2979 fatal("unknown macro variable: '%s'", name);
2983 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
2984 // opr starts with '=' (invariant)
2985 static int expandCB (char *opr, int oprlen) {
2986 char name[257], *p = name, *op = opr;
2988 if (curmacro == NULL) fatal("'=' outside of macro");
2989 if (*op++ != '=') fatal("'=' expected!"); // just in case
2990 //fprintf(stderr, "expand: [%s]\n", opr);
2992 if (!isAlpha(op[0])) return 0; // nothing to do
2994 // copy argument name
2995 while (*op) {
2996 if (isAlphaDigit(*op) || *op == '_') {
2997 if (p-name > 250) fatal("id too long");
2998 *p++ = *op++;
2999 continue;
3001 break;
3003 *p = 0;
3005 // expand argument? we only need to expand `=arg[n]`
3006 op = strSkipSpaces(op);
3007 if (op[0] != '[') return 0;
3009 for (int f = 0; f < curmacro->mac->argc; ++f) {
3010 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3011 if (*op != '[') abort(); // assertion, just in case
3012 // replace argument with character, or with literal value
3013 // used for `=regpair[0]` or `=regpair[]`
3014 if (op[1] == ']') {
3015 op += 2;
3016 const int l = (int)strlen(curmacro->argvals[f]);
3017 const int lleft = (int)strlen(op);
3018 char *tmp = malloc(l+lleft+8);
3019 snprintf(tmp, l+lleft+8, "%s%s", curmacro->argvals[f], op);
3020 if ((int)strlen(tmp) > oprlen) {
3021 free(tmp);
3022 return -1;
3024 strcpy(opr, tmp);
3025 free(tmp);
3026 return 0;
3027 } else {
3028 urasm_exprval_t v;
3029 int error = 0, defined = 1, donteval = 0;
3030 ++op; // skip '['
3031 urasm_exprval_init(&v);
3032 op = (char *)urasm_expr_ex(&v, op, pc, &donteval, &defined, &error);
3033 if (error) return -1;
3034 // result should be a number
3035 if (op == NULL || *op != ']' || v.str != NULL) return -1;
3036 ++op; // skip ']'
3037 // it is guaranteed to have more than one char in opr
3038 // so we can simply put char from argument value, and remove other expression chars
3039 const int l = (int)strlen(curmacro->argvals[f]);
3040 if (v.val < 0) v.val += l; // negative: indexing from the end
3041 if (v.val < 0 || v.val >= l) fatal("index %d is out of bounds for macro argument '%s'", v.val, curmacro->mac->argnames[f]);
3042 // copy char
3043 opr[0] = curmacro->argvals[f][v.val];
3044 // remove other chars
3045 memmove(opr+1, op, strlen(op)+1);
3047 return 0;
3051 fatal("unknown macro variable: '%s'", name);
3055 // main macro expander
3056 static void processMacro (MacroDef *mc) {
3057 SourceLine *oldcurline = curSrcLine;
3058 CurMacroDef cm;
3060 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3061 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3063 // parse macro arguments
3064 cm.mac = mc;
3065 for (int f = 0; f < mc->argc; ++f) {
3066 removeSpaces();
3067 // do we have more arguments?
3068 if (!curLine[0]) {
3069 // nope; check if all args without default values were set
3070 for (int c = f; c < mc->argc; ++c) {
3071 if (mc->argdefaults[c] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[c]);
3072 cm.argvals[c] = strdup(mc->argdefaults[c]);
3074 // get out of argument parsing loop
3075 break;
3077 // check for argument name (this is purely cosmetic thing)
3078 // use like this: "mcall :argname=[value]" (value may be omited for default)
3079 if (curLine[0] == ':' && isAlpha(curLine[1])) {
3080 int pos = 2;
3081 while (isAlphaDigit(curLine[pos]) || curLine[pos] == '_') ++pos;
3082 //hack!
3083 const char svch = curLine[pos];
3084 curLine[pos] = 0;
3085 if (strcasecmp(curLine+1, mc->argnames[f]) != 0) {
3086 // out-of-order, find proper argument
3087 int c = 0;
3088 for (; c < mc->argc; ++c) {
3089 if (c != f && strcasecmp(curLine+1, mc->argnames[c]) == 0) break;
3091 if (c >= mc->argc) fatal("macro '%s' has no argument named '%s'", mc->name, curLine+1);
3092 // rewind
3093 f = c;
3095 curLine[pos] = svch;
3096 // remove argument name
3097 memmove(curLine, curLine+pos, strlen(curLine+pos)+1);
3098 removeSpaces();
3099 // check for '='
3100 if (curLine[0] != '=') fatal("expected '=' after argument name");
3101 // remove '='
3102 memmove(curLine, curLine+1, strlen(curLine));
3103 removeSpaces();
3105 // check for default value
3106 if (curLine[0] == ',') {
3107 if (mc->argdefaults[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
3108 cm.argvals[f] = strdup(mc->argdefaults[f]);
3109 memmove(curLine, curLine+1, strlen(curLine));
3110 continue;
3112 // skip argument (so we will know its length, and will be able to copy it)
3113 char *e = skipArg(curLine);
3114 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3115 const char ech = *e;
3116 if (ech != 0) {
3117 if (f == mc->argc-1 || ech != ',') fatal("invalid macro invocation");
3119 *e = 0;
3120 cm.argvals[f] = strdup(curLine);
3121 if (ech) {
3122 *e = ech;
3123 memmove(curLine, e+1, strlen(e));
3124 } else {
3125 curLine[0] = 0;
3128 // check for line end
3129 removeSpaces();
3130 if (curLine[0]) fatal("invalid macro invocation");
3132 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3134 // insert macro code into the source
3135 setCurSrcLine(mc->lines);
3136 curmacro = &cm;
3137 ++curmacronum;
3138 while (curSrcLine != NULL) {
3139 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3140 processCurrentLine();
3143 setCurSrcLine(oldcurline);
3144 curmacro = NULL;
3145 nextSrcLine();
3149 static MacroDef *findMacro (const char *name) {
3150 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strcasecmp(name, mc->name) == 0) return mc;
3151 return NULL;
3155 static int piMACRO (void) {
3156 char *name;
3157 int argc = 0;
3158 char *argdefaults[32];
3159 char *argnames[32];
3160 SourceLine *stline, *eline = NULL;
3161 MacroDef *mc;
3163 name = strdup(getLabelArg(0));
3164 //fprintf(stderr, "[%s]\n", name);
3165 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
3166 if (curLine[0] == ',') memmove(curLine, curLine+1, strlen(curLine));
3167 removeSpaces();
3169 while (curLine[0]) {
3170 if (argc >= 31) fatal("too many arguments in MACRO");
3171 if (!isAlpha(curLine[0])) fatal("invalid MACRO definition");
3173 argnames[argc] = strdup(getLabelArg(0));
3174 if (curLine[0] == '=') {
3175 // default value
3176 char *e = strchr(curLine, ','), tch;
3178 if (e == NULL) e = curLine+strlen(curLine);
3179 tch = *e;
3180 *e = 0;
3181 argdefaults[argc] = strdup(curLine+1);
3182 *e = tch;
3183 memmove(curLine, e, strlen(e)+1);
3184 } else {
3185 argdefaults[argc] = NULL;
3187 removeSpaces();
3188 if (curLine[0] == ',') {
3189 memmove(curLine, curLine+1, strlen(curLine));
3190 removeSpaces();
3192 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3193 ++argc;
3196 // now find corresponding ENDM
3197 // note that we should skip nested DUPs
3198 stline = curSrcLine;
3199 nextSrcLine(); // skip ourself
3200 while (curSrcLine) {
3201 int inum;
3203 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
3204 // ok, we found something; what is it?
3205 if (inum == 0) {
3206 // new MACRO
3207 fatal("no nested MACROs yet");
3208 } else {
3209 // ENDM, gotcha!
3210 eline = curSrcLine;
3211 // kill ENDM
3212 eline->line[0] = 0;
3213 nextSrcLine(); // skip ENDM
3214 break;
3217 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
3219 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
3220 mc->name = name;
3221 mc->argc = argc;
3222 for (int f = 0; f < argc; ++f) { mc->argdefaults[f] = argdefaults[f]; mc->argnames[f] = argnames[f]; }
3224 eline->next = NULL;
3225 mc->lines = stline->next;
3226 stline->next = curSrcLine;
3227 stline->line[0] = 0;
3229 mc->next = maclist;
3230 maclist = mc;
3231 setCurSrcLine(stline);
3232 return PI_SKIP_LINE;
3236 static int piENDM (void) {
3237 fatal("ENDM without MACRO");
3238 return PI_SKIP_LINE;
3242 ///////////////////////////////////////////////////////////////////////////////
3243 // line processor
3244 static int optWriteType = 't';
3245 static int optWTChanged = 0;
3248 static void piTapParseLoaderName (void) {
3249 if (eatComma()) {
3250 int len;
3251 if (!isStrArg()) fatal("loader name expected");
3252 char *fn = getStrArg(&len);
3253 if (len > 10) fatal("loader name too long");
3254 memset(tapeLoaderName, ' ', 10);
3255 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
3260 static int piMATHMODE (void) {
3261 char *name = getOneLabelArg();
3262 if (strcasecmp(name, "OLD") == 0) urasm_use_old_priorities = 1;
3263 else if (strcasecmp(name, "NEW") == 0) urasm_use_old_priorities = 0;
3264 else fatal("invalid math mode; NEW or OLD expected");
3265 return PI_SKIP_LINE;
3269 static int piDEFFMT (void) {
3270 char *name;
3272 if (isStrArg()) {
3273 int len = 0;
3274 name = getStrArg(&len);
3275 } else {
3276 name = getLabelArg(1);
3278 if (optWTChanged) return 1;
3280 optRunDMB = optRunTape = optRunSCL = 0;
3281 //optRunSNA = 0;
3283 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA") || !strcasecmp(name, "SNARUN")) {
3284 optWriteType = 's';
3285 if (curLine[0]) fatal("too many expressions");
3286 return PI_SKIP_LINE;
3288 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
3289 optWriteType = 't';
3290 piTapParseLoaderName();
3291 return PI_SKIP_LINE;
3293 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE") || !strcasecmp(name, "TAPERUN")) {
3294 optRunTape = 1;
3295 optWriteType = 't';
3296 piTapParseLoaderName();
3297 return PI_SKIP_LINE;
3299 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
3300 optWriteType = 'r';
3301 if (curLine[0]) fatal("too many expressions");
3302 return PI_SKIP_LINE;
3304 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB") || !strcasecmp(name, "DMBRUN")) {
3305 optRunDMB = (name[3] != 0);
3306 optWriteType = 'd';
3307 if (curLine[0]) fatal("too many expressions");
3308 return PI_SKIP_LINE;
3310 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
3311 optWriteType = 'n';
3312 if (curLine[0]) fatal("too many expressions");
3313 return PI_SKIP_LINE;
3315 if (!strcasecmp(name, "SCL") || !strcasecmp(name, "RUNSCL") || !strcasecmp(name, "SCLRUN")) {
3316 optWriteType = 'S';
3317 optRunSCL = (name[3] != 0);
3318 piTapParseLoaderName();
3319 return PI_SKIP_LINE;
3321 fatal("invalid default output type: %s", name);
3325 ///////////////////////////////////////////////////////////////////////////////
3326 // line processor
3328 static void processCurrentLine (void) {
3329 if (!curSrcLine) return; // do nothing
3330 loadCurSrcLine();
3331 processLabel();
3332 for (;;) {
3333 char *str, *ee, name[66];
3334 UrAsmOp *op;
3335 int len;
3336 const char *errpos;
3338 removeSpacesAndColons();
3339 // skip spaces and ':'
3340 if (!curLine[0]) { nextSrcLine(); break; }
3341 // try to find and process command
3342 str = curLine; //while (*str && isSpace(*str)) ++str; // skip spaces
3343 // find command end
3344 for (ee = str; *ee && !isSpace(*ee) && *ee != '"' && *ee != '\'' && *ee != ','; ++ee) {}
3345 // get command, if any
3346 if (ee != str && ee-str <= 64) {
3347 MacroDef *mc;
3349 memset(name, 0, sizeof(name));
3350 memmove(name, str, ee-str);
3351 // known command?
3352 op = urFindOp(name);
3353 if (op) {
3354 // ok, do it
3355 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3356 memmove(curLine, str, strlen(str)+1);
3357 if (op->fn()) {
3358 nextSrcLine(); // skip it
3359 break;
3361 continue;
3363 if ((mc = findMacro(name)) != NULL) {
3364 // ok, do it
3365 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3366 memmove(curLine, str, strlen(str)+1);
3368 processMacro(mc);
3369 //FIXME: allow ':' here?
3370 break;
3374 len = urasm_opasm(curLine, pc, disp, &errpos);
3375 if (len < 0) fatalUrLib(len);
3376 pc += len; disp += len;
3377 if (len >= 0 && errpos) {
3378 memmove(curLine, errpos+1, strlen(errpos));
3379 } else {
3380 nextSrcLine(); // skip it
3381 break;
3387 ///////////////////////////////////////////////////////////////////////////////
3388 // setup instructions
3390 static void registerInstructions (void) {
3391 urAddOp("DISPLAY", piDISPLAY);
3392 urAddOp("DISPLAY0", piDISPLAY0);
3393 urAddOp("DISPLAYA", piDISPLAYA);
3394 urAddOp("DISPHEX", piDISPHEX);
3395 urAddOp("DISPHEX0", piDISPHEX0);
3396 urAddOp("DISPHEXA", piDISPHEXA);
3398 urAddOp("DEFFMT", piDEFFMT);
3399 urAddOp("$MODEL", piMODEL);
3401 urAddOp("MACRO", piMACRO);
3402 urAddOp("ENDM", piENDM);
3404 urAddOp("ORG", piORG);
3405 urAddOp("DISP", piDISP);
3406 urAddOp("ENDDISP", piENDDISP);
3407 urAddOp("PHASE", piDISP);
3408 urAddOp("DEPHASE", piENDDISP);
3409 urAddOp("UNPHASE", piENDDISP);
3410 urAddOp("ALIGN", piALIGN);
3411 urAddOp("DISPALIGN", piDISPALIGN);
3412 urAddOp("PHASEALIGN", piDISPALIGN);
3413 urAddOp("ENT", piENT);
3414 urAddOp("CLR", piCLR);
3415 urAddOp("RESERVE", piRESERVE);
3417 urAddOp("INCLUDE", piINCLUDE);
3418 urAddOp("INCBIN", piINCBIN);
3420 urAddOp("MODULE", piMODULE);
3421 urAddOp("ENDMODULE", piENDMODULE);
3423 urAddOp("DUP", piDUP);
3424 urAddOp("EDUP", piEDUP);
3426 urAddOp("IF", piIF);
3427 urAddOp("IFX", piIFX);
3428 urAddOp("ELSE", piELSE);
3429 urAddOp("ELSEIF", piELSEIF);
3430 urAddOp("ELSEIFX", piELSEIFX);
3431 urAddOp("ENDIF", piENDIF);
3433 urAddOp("DEFINCR", piDEFINCR);
3434 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
3435 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
3436 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
3437 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
3438 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
3439 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
3440 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
3441 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
3443 urAddOp("$ERROR", piERROR);
3444 urAddOp("$WARNING", piWARNING);
3446 urAddOp("$PRINTF", piPRINTF);
3447 urAddOp("$PRINTF0", piPRINTF0);
3448 urAddOp("$PRINTFA", piPRINTFA);
3450 urAddOp("$MATHMODE", piMATHMODE);
3454 ///////////////////////////////////////////////////////////////////////////////
3455 // !0: invalid label
3457 static inline void fnSkipSpaces (const char *expr) {
3458 while (*expr && isSpace(*expr)) ++expr;
3459 return expr;
3464 static inline char fnNextChar (const char *expr) {
3465 while (*expr && isSpace(*expr)) ++expr;
3466 return *expr;
3470 #define FN_SKIP_BLANKS do { \
3471 while (*expr && isSpace(*expr)) ++expr; \
3472 } while (0)
3474 #define FN_CHECK_END do { \
3475 while (*expr && isSpace(*expr)) ++expr; \
3476 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
3477 ++expr; \
3478 } while (0)
3481 #define FN_CHECK_COMMA do { \
3482 while (*expr && isSpace(*expr)) ++expr; \
3483 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
3484 ++expr; \
3485 } while (0)
3488 static const char *readLabelName (char *buf, const char *expr) {
3489 int pos = 0;
3491 while (*expr && isSpace(*expr)) ++expr;
3492 for (;;) {
3493 char ch = *expr++;
3495 if (pos >= 128) return NULL;
3496 if (ch == ')') { --expr; break; }
3497 if (!ch) break;
3498 if (isAlphaDigit(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
3499 buf[pos++] = ch;
3500 } else {
3501 break;
3504 if (pos < 1) return NULL;
3505 buf[pos] = '\0';
3506 if (!urasm_is_valid_name(buf)) return NULL;
3507 return expr;
3511 static const char *fnDefKn (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
3512 char lbl[130];
3514 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
3515 FN_CHECK_END;
3516 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
3517 return expr;
3520 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); }
3521 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); }
3524 static const char *fnAligned256 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3525 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3526 FN_CHECK_END;
3527 if (!donteval) res->val = (res->val%256 ? 0 : 1);
3528 return expr;
3532 static const char *fnSameSeg (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3533 urasm_exprval_t v0, v1;
3535 urasm_exprval_init(&v0);
3536 urasm_exprval_init(&v1);
3537 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
3538 if (*error) return expr;
3539 FN_CHECK_COMMA;
3540 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
3541 if (*error) return expr;
3542 FN_CHECK_END;
3543 if (!donteval) res->val = (v0.val/256 == v1.val/256);
3544 return expr;
3548 static const char *fnAlign (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3549 urasm_exprval_t v0, v1;
3550 urasm_exprval_init(&v0);
3551 urasm_exprval_init(&v1);
3552 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
3553 if (*error) return expr;
3554 FN_SKIP_BLANKS;
3555 if (fnNextChar(expr) == ',') {
3556 FN_CHECK_COMMA;
3557 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
3558 if (*error) return expr;
3559 } else {
3560 v1.val = 256;
3562 FN_CHECK_END;
3563 if (!donteval) {
3564 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
3565 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
3567 return expr;
3571 static const char *fnLow (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3572 const char *ee = expr;
3573 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3574 FN_CHECK_END;
3575 if (!donteval) {
3576 if (res->fixuptype == UR_FIXUP_HIBYTE) {
3577 *error = UR_EXPRERR_FUNC; return ee;
3579 res->fixuptype = UR_FIXUP_LOBYTE;
3580 res->val &= 0xff;
3582 return expr;
3586 static const char *fnHigh (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3587 const char *ee = expr;
3588 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3589 FN_CHECK_END;
3590 if (!donteval) {
3591 if (res->fixuptype == UR_FIXUP_LOBYTE) {
3592 *error = UR_EXPRERR_FUNC; return ee;
3594 res->fixuptype = UR_FIXUP_HIBYTE;
3595 res->val = (res->val>>8)&0xff;
3597 return expr;
3601 static const char *fnAbs (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3602 const char *ee = expr;
3603 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3604 FN_CHECK_END;
3605 if (!donteval) {
3606 if (res->fixuptype != UR_FIXUP_NONE) {
3607 *error = UR_EXPRERR_FUNC; return ee;
3609 res->val = abs(res->val);
3611 return expr;
3615 static const char *fnBSwap (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3616 const char *ee = expr;
3617 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3618 FN_CHECK_END;
3619 if (!donteval) {
3620 if (res->fixuptype != UR_FIXUP_NONE) {
3621 *error = UR_EXPRERR_FUNC; return ee;
3623 res->val = ((res->val>>8)&0xff)|((res->val&0xff)<<8);
3625 return expr;
3629 static const char *fnScrAddr8xn (int nmul, urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3630 int32_t scrbase = 0x4000;
3631 int32_t x, y;
3632 const char *ee = expr;
3633 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
3634 // first argument is `x`
3635 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3636 x = res->val;
3637 // second argument is `y`
3638 FN_CHECK_COMMA;
3639 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3640 y = res->val*nmul;
3641 // optional third arg is screen base
3642 FN_SKIP_BLANKS;
3643 if (expr[0] == ',') {
3644 FN_CHECK_COMMA;
3645 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3646 scrbase = res->val&0xffff;
3648 FN_CHECK_END;
3649 if (!donteval) {
3650 //urasm_exprval_clear(res);
3651 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
3652 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
3653 if (y < 0 || y > 191) fatal("invalid y coordinate: %d", y/nmul);
3654 res->val = scrbase+
3655 (y/64)*2048+
3656 (y%8)*256+
3657 ((y%64)/8)*32+
3659 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
3661 return expr;
3665 static const char *fnScrAddr8x1 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3666 return fnScrAddr8xn(1, res, expr, addr, donteval, defined, error);
3669 static const char *fnScrAddr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3670 return fnScrAddr8xn(8, res, expr, addr, donteval, defined, error);
3674 static const char *fnScrAttr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3675 int32_t scrbase = 0x4000;
3676 int32_t x, y;
3677 const char *ee = expr;
3678 // first argument is `x`
3679 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3680 x = res->val;
3681 // second argument is `y`
3682 FN_CHECK_COMMA;
3683 urasm_exprval_clear(res);
3684 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3685 y = res->val;
3686 // optional third arg is screen base
3687 FN_SKIP_BLANKS;
3688 if (expr[0] == ',') {
3689 FN_CHECK_COMMA;
3690 urasm_exprval_clear(res);
3691 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3692 scrbase = res->val&0xffff;
3694 urasm_exprval_clear(res);
3695 FN_CHECK_END;
3696 if (!donteval) {
3697 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
3698 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
3699 if (y < 0 || y > 23) fatal("invalid y coordinate: %d", y);
3700 res->val = scrbase+6144+y*32+x;
3702 return expr;
3706 static const char *fnArgToStr (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3707 const char *ee = expr;
3708 // argument is macro argument name
3709 while (isSpace(*expr)) ++expr;
3710 if (expr[0] != '=') { *error = UR_EXPRERR_FUNC; return ee; }
3711 ++expr;
3712 if (!isAlpha(expr[0]) && expr[0] != '_') { *error = UR_EXPRERR_FUNC; return ee; }
3713 const char *nend = expr+1;
3714 while (isAlphaDigit(*nend) || *nend == '_') ++nend;
3715 if (nend-expr > 127) { *error = UR_EXPRERR_FUNC; return ee; }
3716 char name[128];
3717 memset(name, 0, sizeof(name));
3718 memcpy(name, expr, nend-expr);
3719 expr = nend;
3720 FN_CHECK_END;
3721 if (curmacro == NULL) fatal("`argtostr` outside of macro");
3722 if (!donteval) {
3723 for (int f = 0; f < curmacro->mac->argc; ++f) {
3724 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3725 // found argument, convert it to string
3726 res->str = realloc(res->str, strlen(curmacro->argvals[f])+1);
3727 strcpy(res->str, curmacro->argvals[f]);
3728 // lowercase it
3729 for (char *s = res->str; *s; ++s) *s = toLower(*s);
3730 //fprintf(stderr, "<%s>\n", res->str);
3731 if (res->str[0]) {
3732 if (res->str[1]) {
3733 res->val = ((unsigned char)res->str[0]);
3734 res->val |= ((unsigned char)res->str[1])<<8;
3735 } else {
3736 res->val = (unsigned char)res->str[0];
3738 } else {
3739 res->val = 0;
3741 return expr;
3744 fatal("unknown macro argument '%s'", name);
3746 return expr;
3750 static void registerFunctions (void) {
3751 urasm_expr_register_func("defined", fnDefined);
3752 urasm_expr_register_func("known", fnKnown);
3753 urasm_expr_register_func("aligned256", fnAligned256);
3754 urasm_expr_register_func("align", fnAlign);
3755 urasm_expr_register_func("sameseg", fnSameSeg);
3756 urasm_expr_register_func("low", fnLow);
3757 urasm_expr_register_func("high", fnHigh);
3758 urasm_expr_register_func("abs", fnAbs);
3759 urasm_expr_register_func("bswap", fnBSwap);
3761 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8);
3762 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1);
3763 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8);
3765 urasm_expr_register_func("marg2str", fnArgToStr);
3769 ///////////////////////////////////////////////////////////////////////////////
3770 // preparing another pass
3772 static void initPass (void) {
3773 curSrcLine = asmText;
3774 curModule = NULL;
3775 pc = start_pc;
3776 disp = start_disp;
3777 ent = start_ent;
3778 inTapeBlock = 0;
3779 tapeXorB = 0;
3780 wasOrg = 0;
3781 wasClr = 0;
3782 ifCount = 0;
3783 defIncr = 0;
3784 lblOptMakeU2 = 0;
3785 curmacronum = 0;
3786 lastSeenGlobalLabel = strdup(" [MAIN] ");
3787 modulesResetSeen();
3788 prepareMemory();
3789 urasm_use_old_priorities = 0;
3793 static int posstPass (void) {
3794 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
3795 if (checkLabels()) return -1;
3796 if (ifCount != 0) fatal("unbalanced IFs");
3797 return 0;
3801 ///////////////////////////////////////////////////////////////////////////////
3802 static int labelCmp (const void *aa, const void *bb) {
3803 if (aa == bb) return 0;
3804 const UrLabelInfo *a = *(const UrLabelInfo **)aa;
3805 const UrLabelInfo *b = *(const UrLabelInfo **)bb;
3806 return
3807 a->value < b->value ? -1 :
3808 a->value > b->value ? 1 :
3813 static void writeLabelsFile (const char *fname) {
3814 if (!fname || !fname[0]) return;
3815 // count labels
3816 int lcount = 0;
3817 for (const UrLabelInfo *ll = labels; ll; ll = ll->next) ++lcount;
3818 UrLabelInfo **larr = NULL;
3819 if (lcount > 0) {
3820 // create labels array
3821 larr = (UrLabelInfo **)malloc(sizeof(UrLabelInfo *)*lcount);
3822 lcount = 0;
3823 for (UrLabelInfo *ll = labels; ll; ll = ll->next, ++lcount) larr[lcount] = ll;
3824 // sort labels
3825 qsort(larr, lcount, sizeof(UrLabelInfo *), &labelCmp);
3827 // write labels
3828 FILE *fo = fopen(fname, "w");
3829 if (lcount > 0) {
3830 for (int f = 0; f < lcount; ++f) {
3831 UrLabelInfo *ll = larr[f];
3832 if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
3833 if (ll->value < 0) {
3834 fprintf(fo, "%d %s\n", ll->value, ll->name);
3835 } else {
3836 fprintf(fo, "#%04X %s\n", (unsigned)ll->value, ll->name);
3839 free(larr);
3841 fclose(fo);
3845 ///////////////////////////////////////////////////////////////////////////////
3846 // options
3848 static struct option longOpts[] = {
3849 {"org", required_argument, NULL, 600},
3850 {"define", required_argument, NULL, 601},
3851 {"defzero", required_argument, NULL, 602},
3852 {"outdir", required_argument, NULL, 660},
3853 {"reffile", required_argument, NULL, 669},
3855 {"sna", 0, NULL, 's'},
3856 {"sna128", 0, NULL, 'S'},
3857 {"tap", 0, NULL, 't'},
3858 {"autotap", 0, NULL, 'T'},
3859 {"raw", 0, NULL, 'r'},
3860 {"autodmb", 0, NULL, 'B'},
3861 {"dmb", 0, NULL, 'b'},
3862 {"none", 0, NULL, 'n'},
3863 {"help", 0, NULL, 'h'},
3864 {"hob", 0, NULL, 'H'},
3865 {"scl", 0, NULL, 'l'},
3866 {"autoscl", 0, NULL, 'L'},
3867 {"fixups", optional_argument, NULL, 'F'},
3869 {NULL, 0, NULL, 0}
3873 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
3876 static void usage (const char *pname) {
3877 printf(
3878 "usage: %s [options] infile\n"
3879 "default infiles:", pname);
3880 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
3881 printf("\n"
3882 "options:\n"
3883 " -s --sna write 48K .SNA file with autostart\n"
3884 " -S --sna128 write 148K .SNA file with autostart\n"
3885 " -t --tap write .tap file\n"
3886 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
3887 " -r --raw write raw file(s)\n"
3888 " -b --dmb write DMB file\n"
3889 " -B --autodmb write DMB file with autostart\n"
3890 " -H --hob write HoBeta code file(s)\n"
3891 " -l --scl write SCL TR-DOS archive\n"
3892 " -L --autoscl write autostarting SCL TR-DOS archive\n"
3893 " -n --none write nothing\n"
3894 " -F --fixups write fixup file 'zfixuptable.txt'\n"
3895 " -h --help this help\n"
3896 "specials:\n"
3897 " --reffile name write labels to reffile, formatted as \"#nnnn NAME\"\n"
3898 " --org xxx set ORG\n"
3899 " --define val perform 'val EQU 1'\n"
3900 " --defzero val perform 'val EQU 0'\n"
3901 " --outdir dir output dir for resulting files (default: current)\n"
3906 ///////////////////////////////////////////////////////////////////////////////
3907 // main
3909 int main (int argc, char *argv[]) {
3910 int res = 0, c;
3911 const char *pname = argv[0];
3912 char *inFile = NULL;
3913 char **defines = NULL, **values = NULL;
3914 int defcount = 0;
3916 initInclideDir();
3918 urasm_getbyte = getByte;
3919 urasm_putbyte = putByte;
3920 urasm_label_by_name = findLabelCB;
3921 urasm_getval = getValueCB;
3922 urasm_expand = expandCB;
3923 urasm_fixup_operand = fixupOperandCB;
3925 //strcpy(tapeLoaderName, "cargador ");
3926 tapeLoaderName[0] = 0;
3928 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
3929 while ((c = getopt_long(argc, argv, "sStTrBbnhHFLl", longOpts, NULL)) >= 0) {
3930 switch (c) {
3931 case '?': return 1;
3932 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
3933 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
3934 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
3935 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
3936 case 'L': optRunSCL = 1; optWriteType = 'S'; optWTChanged = 1; break;
3937 case 'l': optRunSCL = 0; optWriteType = 'S'; optWTChanged = 1; break;
3938 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
3939 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
3940 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
3941 case 'h': usage(pname); res = 0; goto earlyerrquit;
3942 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
3943 case 'F':
3944 optWriteFixups = 1;
3945 if (optarg != NULL) {
3946 if (strcmp(optarg, "asm") == 0 || strcmp(optarg, "zas") == 0) {
3947 optFixupType = 1;
3948 } else if (strcmp(optarg, "asmdiff") == 0 || strcmp(optarg, "zasdiff") == 0) {
3949 optFixupType = 2;
3950 } else if (strcmp(optarg, "asmz") == 0 || strcmp(optarg, "zasz") == 0) {
3951 optFixupType = 3;
3952 } else {
3953 fprintf(stderr, "FATAL: invalid fixup type: '%s'\n", optarg);
3954 return 1;
3957 break;
3958 case 600: // org
3959 c = atoi(optarg);
3960 //fprintf(stderr, "ORG: %d\n", c);
3961 if (c < 0 || c > 65535) {
3962 fprintf(stderr, "FATAL: invalid ORG: %d\n", c);
3963 return 1;
3965 start_pc = start_disp = start_ent = c;
3966 break;
3967 case 601: // define
3968 //fprintf(stderr, "define: [%s]\n", optarg);
3969 defines = realloc(defines, sizeof(char *)*(defcount+1));
3970 values = realloc(values, sizeof(char *)*(defcount+1));
3971 defines[defcount] = strdup(optarg);
3972 values[defcount] = strdup("1");
3973 ++defcount;
3974 break;
3975 case 602: // defzero
3976 //fprintf(stderr, "defzero: [%s]\n", optarg);
3977 defines = realloc(defines, sizeof(char *)*(defcount+1));
3978 values = realloc(values, sizeof(char *)*(defcount+1));
3979 defines[defcount] = strdup(optarg);
3980 values[defcount] = strdup("0");
3981 ++defcount;
3982 break;
3983 case 660: // outdir
3984 if (optOutputDir != NULL) free(optOutputDir);
3985 optOutputDir = strdup(optarg);
3986 break;
3987 case 669: // reffile
3988 if (refFileName) free(refFileName);
3989 refFileName = strdup(optarg);
3990 break;
3994 if (optind >= argc) {
3995 // try to find default input file
3996 for (int f = 0; defInFiles[f]; ++f) {
3997 if (!access(defInFiles[f], R_OK)) {
3998 inFile = strdup(defInFiles[f]);
3999 break;
4002 } else {
4003 inFile = strdup(argv[optind]);
4006 if (!inFile || !inFile[0]) {
4007 res = 1;
4008 fprintf(stderr, "ERROR: no input file!\n");
4009 goto earlyerrquit;
4012 if (optOutputDir == NULL) optOutputDir = strdup(".");
4014 registerInstructions();
4015 registerFunctions();
4017 res = asmTextLoad(inFile);
4018 if (!res) {
4019 for (int f = 0; f < defcount; ++f) {
4020 if (labelDoEQU(defines[f], values[f]) != 0) {
4021 fprintf(stderr, "FATAL: can't define label: '%s'\n", defines[f]);
4022 goto errquit;
4026 for (pass = 0; pass <= 1; ++pass) {
4027 initPass();
4028 printf("pass %d\n", pass);
4029 setCurSrcLine(asmText);
4030 if (setjmp(errJP)) { res = 1; break; }
4031 while (curSrcLine) processCurrentLine();
4032 if (posstPass()) { res = 1; break; }
4035 // write result
4036 if (res == 0) {
4037 char *oc = strdup(inFile);
4038 char *pd = strrchr(oc, '.');
4039 if (pd && !strchr(oc, '/')) *pd = '\0';
4040 switch (optWriteType) {
4041 case 's': saveSna(oc, optSNA48); break;
4042 case 't': saveTap(oc); break;
4043 case 'r': saveRaw(oc); break;
4044 case 'd': saveDMB(oc); break;
4045 case 'H': saveHob(oc); break;
4046 case 'S': saveSCL(oc); break;
4048 free(oc);
4049 if (optWriteFixups) writeFixups();
4050 if (refFileName && refFileName[0]) writeLabelsFile(refFileName);
4052 } else {
4053 fprintf(stderr, "ERROR: loading error!\n");
4056 errquit:
4057 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
4058 clearFixups();
4059 urClearLabels();
4060 modulesClear();
4061 asmTextClear();
4062 urClearOps();
4064 earlyerrquit:
4065 if (inFile) free(inFile);
4066 if (sysIncludeDir) free(sysIncludeDir);
4067 for (int f = defcount-1; f >= 0; --f) {
4068 free(values[f]);
4069 free(defines[f]);
4071 if (defines != NULL) { free(values); free(defines); }
4072 if (optOutputDir != NULL) free(optOutputDir);
4073 return (res ? 1 : 0);