added version info in banner
[urasm.git] / src / urasm.c
bloba2051c9bf41af24268231869adbbfcfc4e8d8993
1 // URASM Z80 assembler
2 // coded by Ketmar // Vamprile Avalon
3 // GPLv3 or later
4 //
5 #include <ctype.h>
6 #include <getopt.h>
7 #include <setjmp.h>
8 #include <stdarg.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <unistd.h>
15 #include "urasmlib/urasmlib.h"
16 #include "ursna48.h"
19 #define VERSION_HI 0
20 #define VERSION_MID 1
21 #define VERSION_LO 0
24 #define MAYBE_UNUSED __attribute__((unused))
26 ///////////////////////////////////////////////////////////////////////////////
27 // global variables
29 static char *sysIncludeDir = NULL;
31 #define MAX_LINE_SIZE 16384
32 static char curLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
35 ///////////////////////////////////////////////////////////////////////////////
36 // init dirs
38 static void initInclideDir (void) {
39 const char *id = getenv("URASM_INCLUDE_DIR");
40 if (id && id[0]) {
41 sysIncludeDir = strdup(id);
42 } else {
43 char buf[128], myDir[4096];
44 pid_t pid = getpid();
45 sprintf(buf, "/proc/%u/exe", (unsigned int)pid);
46 memset(myDir, 0, sizeof(myDir));
47 if (readlink(buf, myDir, sizeof(myDir)-1) < 0) strcpy(myDir, ".");
48 else {
49 char *p = (char *)strrchr(myDir, '/');
50 if (!p) strcpy(myDir, "."); else *p = '\0';
52 strcat(myDir, "/libs");
53 sysIncludeDir = strdup(myDir);
55 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
56 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
60 ///////////////////////////////////////////////////////////////////////////////
61 // string utilities
63 /*TODO: expression parser should understant escapes in strings!*/
64 /* trim trailing spaces and comments */
65 static void normalizeStr (char *s) {
66 char *p, inQ = 0;
67 int f;
68 // now skip all shit
69 for (p = s; *p; ++p) {
70 if (inQ) {
71 // inside the string
72 if (*p == '\\') {
73 switch (*(++p)) {
74 case 'x': case 'X': // hex code
75 ++p; // skip 'x'
76 for (f = 0; f < 2; ++f) if (!isxdigit(*p)) break;
77 --p; // last digit will be skiped by 'for'
78 break;
79 case '0': // octal code
80 for (f = 0; f < 4; ++f) if (!isdigit(*p) || *p > '7') break;
81 --p; // last digit will be skiped by 'for'
82 break;
83 case '1' ... '9': // decimal code
84 for (f = 0; f < 3; ++f) if (!isdigit(*p)) break;
85 --p; // last digit will be skiped by 'for'
86 break;
87 default: ; // other escapes, do nothing
89 } else {
90 if (*p == inQ) inQ = 0;
92 continue;
94 // outside the string
95 switch (*p) {
96 case '"': case '\'': // string catched
97 inQ = *p;
98 break;
99 case ';': // end of line, comment follows
100 *p-- = '\0'; // strip it and quit
101 break;
102 default: ; // do nothing
105 // trim right
106 for (p = s+strlen(s)-1; p >= s && isspace(*p); --p) ;
107 p[1] = '\0';
111 /* returns NULL or pointer to args */
112 /* skips spaces after command if any */
113 static char *strIsCommand (const char *command, char *str) {
114 int cnt;
116 for (cnt = 1; cnt > 0; --cnt) {
117 while (*str && isspace(*str)) ++str; // skip spaces
118 for (; *command && *str; ++command, ++str) {
119 if (toupper(*command) != toupper(*str)) return NULL; // alas
121 if (*command) return NULL; // alas
122 if (*str && isalnum(*str)) return NULL; // alas
123 while (*str && isspace(*str)) ++str; // skip spaces
124 if (*str && *str == ':') break; // try again if we have a colon
125 return str; // found
127 return NULL;
131 /* don't free() result */
132 /* skips trailing spaces */
133 static char *parseStr (char **str, char endQ, int *lenp) {
134 static char buf[MAX_LINE_SIZE];
135 int len = 0, n, f, base;
136 char *a = *str;
138 int xDigit (char ch, int base) {
139 if (ch < '0') return -1;
140 if (base <= 10) {
141 if (ch >= '0'+base) return -1;
142 return ch-'0';
144 ch = toupper(ch);
145 if (ch <= '9') return ch-'0';
146 if (ch < 'A' || ch > 'A'+base-10) return -1;
147 ch -= 'A'-10;
148 return ch<base ? ch : -1;
151 if (lenp) *lenp = 0;
152 for (; *a; ++a) {
153 if (*a == '\\') {
154 if (!a[1]) break;
155 switch (*(++a)) {
156 case 'a': buf[len++] = '\a'; break;
157 case 'b': buf[len++] = '\b'; break;
158 case 'f': buf[len++] = '\f'; break;
159 case 'n': buf[len++] = '\n'; break;
160 case 'r': buf[len++] = '\r'; break;
161 case 't': buf[len++] = '\t'; break;
162 case 'v': buf[len++] = '\v'; break;
163 case 'z': buf[len++] = '\0'; break;
164 case 'x': case 'X': // hex
165 ++a; // skip 'x'
166 base = 16; f = 2;
167 donum: for (n = 0; f > 0; --f) {
168 char ch = xDigit(*a++, base);
169 if (ch < 0) break;
170 n *= base;
171 n += ch;
173 buf[len++] = n;
174 --a; // return to the last digit, 'for' will skip it
175 break;
176 case '0': // octal
177 base = 8; f = 4;
178 goto donum;
179 case '1' ... '9': // decimal
180 base = 10; f = 3;
181 goto donum;
182 default: buf[len++] = a[0]; break; // others
184 } else {
185 if (*a == endQ) { ++a; break; }
186 buf[len++] = *a;
189 while (*a && isspace(*a)) ++a; // skip trailing spaces
190 *str = a;
191 buf[len] = '\0';
192 if (lenp) *lenp = len;
193 return buf;
197 ///////////////////////////////////////////////////////////////////////////////
198 // source file stack, reader, etc
200 typedef struct SourceLine SourceLine;
201 struct SourceLine {
202 SourceLine *next;
203 char *line;
204 char *fname;
205 int lineNo;
208 static SourceLine *asmText = NULL;
209 static SourceLine *asmTextLast = NULL;
210 static SourceLine *curSrcLine = NULL;
213 static void asmTextClear (void) {
214 while (asmText) {
215 SourceLine *l = asmText;
216 asmText = asmText->next;
217 free(l->line);
218 free(l->fname);
219 free(l);
221 asmTextLast = curSrcLine = NULL;
225 static int asmTextLoad (const char *fname) {
226 FILE *fl;
227 int lineNo = 0;
228 char *args;
229 SourceLine *s;
230 static int includeCount = 0;
232 if (!(fl = fopen(fname, "r"))) {
233 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
234 return -1;
236 printf("loading: %s\n", fname);
237 // read file
238 while (fgets(curLine, sizeof(curLine)-1, fl)) {
239 ++lineNo;
240 curLine[sizeof(curLine)-1] = '\0';
241 normalizeStr(curLine);
242 if (!curLine[0]) continue; // don't store empty lines
243 // find specials, if any
244 if (isspace(curLine[0])) {
245 if ((args = strIsCommand("INCLUDE", curLine)) != NULL) {
246 // process 'INCLUDE'
247 int system = 0;
248 char qCh = 0;
250 if (!args[0]) {
251 fclose(fl);
252 fprintf(stderr, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname, lineNo);
253 return -1;
255 if (args[0] == '"' || args[0] == '\'') qCh = *args++;
256 else if (args[0] == '<') { qCh = '>'; ++args; system = 1; }
257 char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
258 if (!fn[0]) {
259 fclose(fl);
260 fprintf(stderr, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname, lineNo);
261 return -1;
263 if (args[0]) {
264 fclose(fl);
265 fprintf(stderr, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname, lineNo);
266 return -1;
268 if (includeCount > 256) {
269 fclose(fl);
270 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname, lineNo);
271 return -1;
273 if (system) sprintf(curLine, "%s/%s", sysIncludeDir, fn); else sprintf(curLine, "%s", fn);
274 fn = strdup(curLine); if (!fn) abort();
275 ++includeCount;
276 system = asmTextLoad(fn);
277 --includeCount;
278 free(fn);
279 if (system) { fclose(fl); return system; }
280 continue;
283 // add current line
284 s = calloc(1, sizeof(SourceLine));
285 if (!s) abort();
286 s->lineNo = lineNo;
287 s->line = strdup(curLine); if (!s->line) abort();
288 s->fname = strdup(fname); if (!s->fname) abort();
289 if (asmTextLast) asmTextLast->next = s; else asmText = s;
290 asmTextLast = s;
292 fclose(fl);
293 return 0;
297 static inline void loadCurSrcLine (void) { if (curSrcLine) strcpy(curLine, curSrcLine?curSrcLine->line:""); }
298 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
299 static inline SourceLine *nextSrcLine (void) { return curSrcLine ? setCurSrcLine(curSrcLine->next) : NULL; }
302 ///////////////////////////////////////////////////////////////////////////////
303 // prototypes
305 static void processCurrentLine (void); // only one, will skip to next one
308 ///////////////////////////////////////////////////////////////////////////////
309 // error raisers, etc
311 static jmp_buf errJP;
314 static void errorWriteFile (void) {
315 if (curSrcLine) {
316 fprintf(stderr, "at file %s, line %d: ", curSrcLine->fname, curSrcLine->lineNo);
317 } else {
318 fprintf(stderr, "somewhere in time: ");
322 static void errorMsgV (const char *fmt, va_list ap) {
323 vfprintf(stderr, fmt, ap);
324 va_end(ap);
325 fputc('\n', stderr);
326 fflush(stderr);
330 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
331 va_list ap;
332 fprintf(stderr, "WARNING ");
333 errorWriteFile();
334 va_start(ap, fmt);
335 errorMsgV(fmt, ap);
339 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
340 va_list ap;
341 fprintf(stderr, "FATAL ");
342 errorWriteFile();
343 va_start(ap, fmt);
344 errorMsgV(fmt, ap);
348 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
349 va_list ap;
350 va_start(ap, fmt);
351 errorMsgV(fmt, ap);
352 longjmp(errJP, 666);
356 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
357 errorMsg("%s", urErrorMessage(errcode));
358 longjmp(errJP, 666);
362 //////////////////////////////////////////////////////////////////////////////
363 // operator management
365 typedef void (*UrAsmOpFn) (void);
367 typedef struct UrAsmOp UrAsmOp;
368 struct UrAsmOp {
369 char *name;
370 UrAsmOpFn fn;
371 UrAsmOp *next;
374 static UrAsmOp *oplist = NULL;
377 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
378 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
379 if (!res) abort();
380 res->name = strdup(name);
381 res->fn = fn;
382 res->next = oplist;
383 oplist = res;
384 return res;
388 static UrAsmOp *urFindOp (const char *name) {
389 UrAsmOp *res;
390 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
391 return res;
395 static void urClearOps (void) {
396 UrAsmOp *c;
397 while (oplist) {
398 c = oplist; oplist = oplist->next;
399 free(c->name);
400 free(c);
405 ///////////////////////////////////////////////////////////////////////////////
406 // label management
408 typedef struct UrLabelInfo UrLabelInfo;
409 struct UrLabelInfo {
410 char *name;
411 int32_t value;
412 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
413 int known; /* !0: label value already known */
414 int refLine; /* first referenced line */
415 char *refFile;
416 UrLabelInfo *next;
419 static UrLabelInfo *labels = NULL;
421 static void urClearLabels (void) {
422 UrLabelInfo *c;
423 while ((c = labels)) {
424 labels = labels->next;
425 if (c->name) free(c->name);
426 if (c->refFile) free(c->refFile);
427 free(c);
432 static UrLabelInfo *urFindLabel (const char *name) {
433 UrLabelInfo *c;
434 for (c = labels; c; c = c->next) if (!strcmp(name, c->name)) break;
435 return c;
439 static UrLabelInfo *urAddLabel (const char *name) {
440 UrLabelInfo *c = urFindLabel(name);
441 if (!c) {
442 UrLabelInfo *p;
443 for (p = NULL, c = labels; c; p = c, c = c->next) ;
444 c = calloc(1, sizeof(UrLabelInfo));
445 if (!c) abort();
446 c->name = strdup(name);
447 c->type = -1;
448 if (p) p->next = c; else labels = c;
449 c->next = NULL;
451 return c;
455 ///////////////////////////////////////////////////////////////////////////////
456 // module list management
458 typedef struct ModuleInfo ModuleInfo;
459 struct ModuleInfo {
460 char *name;
461 char *fname; // opened in this file
462 int seen; // !0: module already seen, skip other definitions from the same file
463 ModuleInfo *next;
465 static ModuleInfo *modules = NULL;
466 static ModuleInfo *curModule = NULL;
469 static void modulesClear (void) {
470 curModule = NULL;
471 while (modules) {
472 ModuleInfo *c = modules;
473 modules = modules->next;
474 free(c->name);
475 free(c->fname);
476 free(c);
481 static void modulesResetSeen (void) {
482 ModuleInfo *c;
483 for (c = modules; c; c = c->next) c->seen = 0;
487 static ModuleInfo *moduleFind (const char *name) {
488 ModuleInfo *c;
489 if (!name || !name[0]) return NULL;
490 for (c = modules; c; c = c->next) if (!strcmp(c->name, name)) break;
491 return c;
495 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
496 ModuleInfo *c;
497 if (!name || !fname || !name[0] || !fname[0]) abort();
498 c = calloc(1, sizeof(ModuleInfo));
499 if (!c) abort();
500 c->name = strdup(name); if (!c->name) abort();
501 c->fname = strdup(fname); if (!c->fname) abort();
502 c->next = modules;
503 modules = c;
504 return c;
508 ///////////////////////////////////////////////////////////////////////////////
509 // destination memory management
511 static uint8_t memory[65536];
512 static char memused[65536];
513 static uint16_t pc = 0; /* current position to write */
514 static uint16_t disp = 0; /* current 'virtual PC' */
515 static uint16_t ent = 0; /* eng */
516 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
517 static int inTapeBlock = 0;
518 static uint8_t tapeXorB = 0;
521 static uint8_t getByte (uint16_t addr) {
522 return memory[addr];
526 static __attribute__((unused)) uint16_t getWord (uint16_t addr) {
527 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
531 static void putByte (uint16_t addr, uint8_t b) {
532 if (inTapeBlock) tapeXorB ^= b;
533 memory[addr] = b;
534 memused[addr] = 1;
538 static MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
539 putByte(addr, w&0xFFU);
540 putByte(addr+1, (w>>8)&0xFFU);
544 static void emitByte (uint8_t b) {
545 putByte(pc, b);
546 ++pc; ++disp;
550 static void emitWord (uint16_t w) {
551 emitByte(w&0xFFU);
552 emitByte((w>>8)&0xFFU);
556 static void emitRWord (uint16_t w) {
557 emitByte((w>>8)&0xFFU);
558 emitByte(w&0xFFU);
562 static void prepareMemory (void) {
563 memset(memory, 0, sizeof(memory));
564 memset(memused, 0, sizeof(memused));
568 ///////////////////////////////////////////////////////////////////////////////
569 // label getter and utilities
571 static char *lastSeenGlobalLabel = NULL; /* global */
574 static char *fixLocalLabel (const char *name) {
575 static char newname[MAX_LINE_SIZE*2+1024];
577 if (!name || !name[0]) newname[0] = '\0';
578 else if (!lastSeenGlobalLabel || name[0] != '.') strcpy(newname, name);
579 else {
580 // this is local label, let's rename it
581 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
583 return newname;
587 static char *fixGlobalLabel (const char *name) {
588 static char newname[MAX_LINE_SIZE*2+1024];
590 if (!name || !name[0]) newname[0] = '\0';
591 else if (!curModule || name[0] == '.' || name[0] == '{' || strchr(name, '.')) strcpy(newname, name);
592 else {
593 // this is global unqualified label and we have a module; let's rename it
594 sprintf(newname, "%s.%s", curModule->name, name);
596 return newname;
600 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found) {
601 UrLabelInfo *lbl;
602 char *ln, *nn;
604 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
605 lbl = urFindLabel(nn);
606 if (!lbl) {
607 if (pass != 0) {
608 errorMsg("using undefined label %s", ln);
609 *found = 0;
610 *defined = 0;
611 return 0;
613 lbl = urAddLabel(nn);
614 lbl->type = -1;
615 lbl->known = 0;
616 lbl->refLine = curSrcLine->lineNo;
617 lbl->refFile = strdup(curSrcLine->fname);
618 //printf("new label: [%s]\n", lbl->name);
619 } else {
620 //printf("label reference: [%s]\n", lbl->name);
622 if (lbl) {
623 *found = 1;
624 *defined = lbl->known!=0;
625 return lbl->value;
627 *found = 0;
628 *defined = 0;
629 return 0;
633 static int checkLabels (void) {
634 UrLabelInfo *c;
635 int wasError = 0;
637 for (c = labels; c; c = c->next) {
638 if (c->type == -1) {
639 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
640 wasError = 1;
642 if (c->type == 0) c->known = -1;
644 //if (wasError) longjmp(errJP, 667);
645 return wasError;
649 ///////////////////////////////////////////////////////////////////////////////
650 // expression utils
652 static int32_t getExprArg (int *defined) {
653 int error = 0;
654 char *a = curLine;
655 int32_t res;
657 while (*a && isspace(*a)) ++a;
658 if (!a[0]) fatal("expression expected");
659 const char *ee = urExpression(&res, a, disp, defined, &error);
660 if (error) fatalUrLib(error);
661 if (*ee) {
662 if (ee[0] != ',') fatal("bad expression");
663 memmove(curLine, ee, strlen(ee)+1);
664 } else {
665 curLine[0] = '\0';
667 return res;
671 static int32_t getOneExprArg (int *defined) {
672 int32_t res = getExprArg(defined);
673 if (curLine[0]) fatal("too many expressions");
674 return res;
678 static int isStrArg (void) { return curLine[0]=='"' || curLine[0] == '\''; }
681 static char *getStrArg (int *lenp) {
682 char *res, qCh;
683 char *a = curLine;
685 while (*a && isspace(*a)) ++a;
686 qCh = *a++;
687 if (qCh != '"' && qCh != '\'') fatal("string expected");
688 res = parseStr(&a, qCh, lenp);
689 if (*a) {
690 if (a[0] != ',') fatal("bad string expression");
691 memmove(curLine, a, strlen(a)+1);
692 } else {
693 curLine[0] = '\0';
695 return res;
699 static MAYBE_UNUSED char *getOneStrArg (int *lenp) {
700 char *res = getStrArg(lenp);
701 if (curLine[0]) fatal("too many expressions");
702 return res;
706 static char *getLabelArg (void) {
707 static char res[MAX_LINE_SIZE+128], *p = res;
708 char *a = curLine;
710 while (*a && isspace(*a)) ++a;
711 if (!a[0]) fatal("label expected");
712 for (; *a && a[0] != ','; ++a) *p++ = *a;
713 for (; p > res && isspace(p[-1]); --p) ;
714 *p = '\0';
715 if (p-res > 120) fatal("label name too long: %s", res);
716 while (*a && isspace(*a)) ++a;
717 if (*a) {
718 if (a[0] != ',') fatal("bad string expression");
719 memmove(curLine, a, strlen(a)+1);
720 } else {
721 curLine[0] = '\0';
723 return res;
727 static char *getOneLabelArg (void) {
728 char *res = getLabelArg();
729 if (curLine[0]) fatal("too many expressions");
730 return res;
734 ///////////////////////////////////////////////////////////////////////////////
735 // label processor
737 static MAYBE_UNUSED void removeSpacesAndColons (void) {
738 char *ep = curLine;
739 while (*ep && (isspace(*ep) || *ep == ':')) ++ep;
740 memmove(curLine, ep, strlen(ep)+1);
744 /* remove label from curLine */
745 static void removeLabel (void) {
746 char *ep = curLine;
748 if (ep[0] && !isspace(ep[0])) for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ; // skip text
749 // skip spaces and colons
750 while (*ep && (isspace(*ep) || *ep == ':')) ++ep;
751 memmove(curLine, ep, strlen(ep)+1);
755 static void processLabel (void) {
756 char *argstart;
757 char *ep, *ln, *nn;
758 static char n2[256];
759 UrLabelInfo *lbl;
760 int noLocAff = 0, doEQU = 0;
762 if (!curLine[0] || isspace(curLine[0]) || curLine[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
763 // collect label
764 for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ;
765 if (ep-curLine > 120) fatal("label too long");
766 // copy label
767 memset(n2, 0, sizeof(n2));
768 memmove(n2, curLine, ep-curLine);
769 if (!urIsValidLabelName(n2)) return; // this is not a valid label, get out of here
770 // check if this can be instruction
771 while (*ep && isspace(*ep)) ++ep;
772 if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
773 // ok, we got a good label
774 removeLabel();
775 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
776 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
777 lbl = urAddLabel(nn);
778 if (!lbl->refFile) {
779 lbl->refLine = curSrcLine->lineNo;
780 lbl->refFile = strdup(curSrcLine->fname);
782 //printf("%d: new: [%s]\n", localLbl, lbl->name);
783 // get command name
784 if (curLine[0] == '=') {
785 doEQU = 0;
786 argstart = curLine+1;
787 } else {
788 doEQU = 1;
789 argstart = strIsCommand("EQU", curLine);
791 if (!argstart || doEQU) {
792 if (pass == 0 && lbl->type != -1) fatal("duplicate label %s", lbl->name);
794 if (argstart) {
795 // do '=' or 'EQU'
796 memmove(curLine, argstart, strlen(argstart)+1);
797 if (!doEQU) {
798 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label %s", lbl->name);
800 int defined = 1;
801 int32_t res = getOneExprArg(&defined);
802 lbl->type = doEQU;
803 if (defined) {
804 lbl->value = res;
805 lbl->known = 1;
806 } else {
807 if (pass != 0) fatal("can't calculate label %s", lbl->name);
809 curLine[0] = '\0';
810 return;
812 // code label
813 if (lbl->name[0] != '{' && !noLocAff) {
814 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
815 lastSeenGlobalLabel = strdup(lbl->name);
817 lbl->type = 2;
818 lbl->value = disp;
819 lbl->known = 1;
823 ///////////////////////////////////////////////////////////////////////////////
824 // instruction finder (in source)
826 /* array ends with NULL */
827 /* returns line or NULL */
828 /* iidx will be set to found instruction number */
829 static SourceLine *findNextInstructionFromList (int *iidx, ...) {
830 va_list ap;
831 SourceLine *cur = curSrcLine;
833 if (iidx) *iidx = -1;
834 for (; cur; cur = cur->next) {
835 int f;
836 if (!isspace(cur->line[0])) continue; // fuck labeled strings
837 va_start(ap, iidx);
838 for (f = 0;;++f) {
839 const char *name = va_arg(ap, const char *);
840 if (!name) break;
841 if (strIsCommand(name, cur->line)) {
842 va_end(ap);
843 if (iidx) *iidx = f;
844 return cur;
847 va_end(ap);
849 return NULL;
853 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
854 return findNextInstructionFromList(NULL, name, NULL);
858 ///////////////////////////////////////////////////////////////////////////////
859 // writers
861 static int optRunSNA = 0;
862 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
865 /* return 'found' flag */
866 static int findChunkFrom (int addr, int *start, int *len) {
867 if (addr < 0) addr = 0;
868 for (; addr <= 65535 && !memused[addr]; ++addr) ;
869 if (addr > 65535) return 0;
870 *start = addr;
871 for (; addr <= 65535 && memused[addr]; ++addr) ;
872 *len = addr-(*start);
873 return 1;
877 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
878 for (; buflen >= 2; buflen -= 2) {
879 int cnt = *buf++;
880 uint8_t byte = *buf++;
881 //printf("%d %u %u\n", cnt+1, byte, addr);
882 for (; cnt >= 0; --cnt, ++addr) if (!memused[addr]) putByte(addr, byte);
887 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
888 if (fwrite(&b, 1, 1, fo) != 1) return -1;
889 if (bxor) *bxor = (*bxor)^b;
890 return 0;
894 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
895 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
896 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
897 return 0;
901 ///////////////////////////////////////////////////////////////////////////////
902 // .sna
904 static int saveSna (const char *fname) {
905 char *fn = malloc(strlen(fname)+16);
906 uint8_t regs[27];
907 FILE *fo;
909 sprintf(fn, "%s.sna", fname);
910 fo = fopen(fn, "wb");
911 free(fn);
912 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
913 memmove(regs, ursna48, 27);
914 if (optRunSNA) {
915 /* push new address */
916 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
917 sp -= 2;
918 putByte(sp, ent&0xFFU);
919 putByte(sp+1, (ent>>8)&0xFFU);
920 regs[23] = sp&0xFFU;
921 regs[24] = (sp>>8)&0xFFU;
923 fwrite(regs, 27, 1, fo);
924 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
925 fwrite(memory+16384, 49152, 1, fo);
926 fclose(fo);
927 return 0;
931 ///////////////////////////////////////////////////////////////////////////////
932 // bin chunks
934 static void saveRaw (const char *basename) {
935 char *fname = malloc(strlen(basename)+16);
936 int start = 0, len;
937 FILE *fo;
939 while (findChunkFrom(start, &start, &len)) {
940 sprintf(fname, "%s_%04X.%s", basename, start, optTapExt?"tap":"bin");
941 fo = fopen(fname, "wb");
942 if (!fo) {
943 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
944 } else {
945 printf("out: %s\n", fname);
946 if (fwrite(memory+start, len, 1, fo) != 1) {
947 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
948 fclose(fo);
949 unlink(fname);
950 } else {
951 fclose(fo);
954 start += len;
956 free(fname);
960 ///////////////////////////////////////////////////////////////////////////////
961 // .tap
963 static void saveTap (const char *basename) {
964 char *fname = malloc(strlen(basename)+16);
965 char blkname[128];
966 int start = 0, len, f;
967 uint8_t bxor;
968 FILE *fo;
970 sprintf(fname, "%s.tap", basename);
971 fo = fopen(fname, "wb");
972 free(fname);
973 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
974 while (findChunkFrom(start, &start, &len)) {
975 // write header
976 sprintf(blkname, "c%04X:%04X", start, len);
977 printf(" block: %s\n", blkname);
978 fWriteWord(fo, 19, NULL); // length of header
979 bxor = 0;
980 fWriteByte(fo, 0, &bxor); // header block
981 fWriteByte(fo, 3, &bxor); // 'code' flag
982 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
983 fWriteWord(fo, len, &bxor);
984 fWriteWord(fo, start, &bxor);
985 fWriteWord(fo, 32768, &bxor);
986 fWriteByte(fo, bxor, NULL);
987 // write data
988 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
989 bxor = 0;
990 fWriteByte(fo, 0xFFU, &bxor); // data block
991 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
992 fWriteByte(fo, bxor, NULL);
993 start += len;
998 ///////////////////////////////////////////////////////////////////////////////
999 // pseudoinstructions
1001 // note that processCurrentLine() will NOT skip to the next line before
1002 // calling pseudoinstruction handler!
1003 // note that processCurrentLine() will skip current line after calling
1004 // pseudoinstruction handler!
1006 static int wasOrg = 0;
1009 ///////////////////////////////////////////////////////////////////////////////
1010 // user warnings
1012 static void piDISPLAYX (int passNo) {
1013 for (;;) {
1014 if (isStrArg()) {
1015 int len = 0;
1016 char *res = getStrArg(&len);
1017 if (passNo < 0 || pass == passNo) printf("%s", res);
1018 } else {
1019 int defined = 1;
1020 int32_t v = getExprArg(&defined);
1021 if (passNo < 0 || pass == passNo) printf("%d", v);
1023 if (!curLine[0]) break;
1024 if (curLine[0] != ',') fatal("invalid expression");
1025 memmove(curLine, curLine+1, strlen(curLine));
1030 static void piDISPLAY (void) { piDISPLAYX(1); }
1031 static void piDISPLAY0 (void) { piDISPLAYX(0); }
1032 static void piDISPLAYA (void) { piDISPLAYX(-1); }
1035 ///////////////////////////////////////////////////////////////////////////////
1036 // ORG, DISP, etc.
1038 static void piORG (void) {
1039 int defined = 1;
1040 int32_t res = getOneExprArg(&defined);
1041 if (!defined) fatal("sorry, ORG operand value must be known here");
1042 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
1043 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
1044 pc = disp = res;
1045 if (!wasOrg) {
1046 wasOrg = 1;
1047 ent = res;
1052 static void piDISP (void) {
1053 int defined = 1;
1054 int32_t res = getOneExprArg(&defined);
1055 if (!defined) fatal("sorry, DISP operand value must be known here");
1056 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
1057 //printf("DISP=%d\n", res);
1058 disp = res;
1062 static void piENDDISP (void) {
1063 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1064 disp = pc;
1068 static void piENT (void) {
1069 int defined = 1;
1070 int32_t res = getOneExprArg(&defined);
1071 if (!defined) fatal("sorry, ENT operand value must be known here");
1072 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
1073 ent = res;
1077 static void piALIGN (void) {
1078 int defined = 1;
1079 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1080 int32_t res = getOneExprArg(&defined);
1081 if (!defined) fatal("sorry, ALIGN operand value must be known here");
1082 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
1083 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
1084 if (res > 0 && pc%res != 0) {
1085 pc /= res;
1086 pc *= res;
1087 pc += res;
1088 disp = pc;
1093 static void piDISPALIGN (void) {
1094 int defined = 1;
1095 int32_t res = getOneExprArg(&defined);
1096 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
1097 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
1098 if (res > 0 && disp%res != 0) {
1099 disp /= res;
1100 disp *= res;
1101 disp += res;
1106 ///////////////////////////////////////////////////////////////////////////////
1107 // DEFx
1109 static void piDEFBW (int isWord) {
1110 for (;;) {
1111 int defined = 1;
1112 defined = 0;
1113 int32_t res = getExprArg(&defined);
1114 if (pass > 0 && !defined) fatal("undefined operand");
1115 if (isWord) {
1116 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
1117 if (res < 0) res += 65536;
1118 if (isWord == 1) emitWord(res); else emitRWord(res);
1119 } else {
1120 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
1121 if (res < 0) res += 256;
1122 emitByte(res);
1124 if (!curLine[0]) break;
1125 if (curLine[0] != ',') fatal("invalid operand: ',' missing");
1126 memmove(curLine, curLine+1, strlen(curLine));
1130 static void piDEFB (void) { piDEFBW(0); }
1131 static void piDEFW (void) { piDEFBW(1); }
1132 static void piDEFR (void) { piDEFBW(2); }
1135 static void piDEFS (void) {
1136 for (;;) {
1137 int32_t bt, f;
1138 int defined = 0;
1139 int32_t res = getExprArg(&defined);
1140 if (pass > 0 && !defined) fatal("undefined operand");
1141 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
1142 if (*curLine && curLine[0] == ',') {
1143 memmove(curLine, curLine+1, strlen(curLine));
1144 bt = getExprArg(&defined);
1145 if (pass > 0 && !defined) fatal("undefined operand");
1146 if (bt < -127 || bt > 255) fatal("invalid operand value: %d", res);
1147 if (bt < 0) bt += 256;
1148 for (f = 0; f < res; ++f) emitByte(bt);
1149 } else {
1150 pc += res; disp += res;
1152 if (!curLine[0]) break;
1153 if (curLine[0] != ',') fatal("invalid operand: ',' missing");
1154 memmove(curLine, curLine+1, strlen(curLine));
1159 /* bit 0: put '\0' */
1160 /* bit 1: set bit 7 of last byte */
1161 /* bit 2: put length */
1162 static void piDEFSTR (int type) {
1163 for (;;) {
1164 if (isStrArg()) {
1165 int f, len = 0;
1166 char *res = getStrArg(&len);
1167 if (type&0x04) {
1168 if (len > 255) fatal("string too long");
1169 emitByte(len);
1171 for (f = 0; f < len; ++f) {
1172 uint8_t b = (uint8_t)res[f];
1173 if ((type&0x01) && f == len-1) b |= 0x80;
1174 emitByte(b);
1176 if (type&0x01) emitByte(0);
1177 } else {
1178 int defined = 1;
1179 int32_t v = getExprArg(&defined);
1180 if (pass > 0 && !defined) fatal("undefined expression");
1181 if (!defined) v = 0;
1182 emitByte(v);
1184 if (!curLine[0]) break;
1185 if (curLine[0] != ',') fatal("invalid expression");
1186 memmove(curLine, curLine+1, strlen(curLine));
1191 static void piDEFM (void) { piDEFSTR(0x00); }
1192 static void piDEFZ (void) { piDEFSTR(0x01); }
1193 static void piDEFX (void) { piDEFSTR(0x02); }
1194 static void piDEFC (void) { piDEFSTR(0x04); }
1197 ///////////////////////////////////////////////////////////////////////////////
1198 // INCBIN
1200 /* INCBIN "name"[,maxlen] */
1201 static void piINCBIN (void) {
1202 int system = 0;
1203 char *fn, qCh;
1204 uint8_t bt;
1205 FILE *fl;
1206 int maxlen = 65536;
1207 char *args = curLine;
1209 if (!curLine[0]) fatal("INCBIN without file name");
1210 if (isStrArg()) {
1211 qCh = *args++;
1212 system = 0;
1213 } else if (curLine[0] == '<') {
1214 qCh = '<'; ++args;
1215 system = 1;
1216 } else {
1217 qCh = 0;
1218 system = 0;
1220 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
1221 if (!fn[0]) fatal("INCBIN: empty file name");
1222 memmove(curLine, args, strlen(args)+1);
1223 // maxlen
1224 if (curLine[0] == ',') {
1225 memmove(curLine, curLine+1, strlen(curLine));
1226 int defined = 1;
1227 maxlen = getOneExprArg(&defined);
1228 if (!defined) fatal("INCBIN: undefined maxlen");
1229 if (maxlen < 1) return; // nothing to do
1231 // now fix name
1232 if (system) {
1233 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
1234 } else {
1235 sprintf(curLine, "%s", fn);
1238 fl = fopen(curLine, "rb");
1239 if (!fl) fatal("INCBIN: file not found: %s", curLine);
1240 while (maxlen-- > 0) {
1241 int res = fread(&bt, 1, 1, fl);
1242 if (!res) break;
1243 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: %s", curLine); }
1244 emitByte(bt);
1246 fclose(fl);
1250 ///////////////////////////////////////////////////////////////////////////////
1251 // MODULE, ENDMODULE
1253 static void piENDMODULE (void) {
1254 if (!curModule) fatal("ENDMODULE without MODULE");
1255 if (curLine[0]) {
1256 char *mn = getOneLabelArg();
1257 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE");
1259 curModule = NULL;
1263 static void piMODULE (void) {
1264 ModuleInfo *mi;
1265 char *mn;
1266 SourceLine *ol = curSrcLine;
1267 int inum;
1269 if (curModule) fatal("no nested modules allowed");
1270 mn = getOneLabelArg();
1271 if (!urIsValidLabelName(mn)) fatal("invalid module name: %s", mn);
1272 mi = moduleFind(mn);
1273 if (mi) {
1274 if (mi->seen) {
1275 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
1276 /* skip module */
1277 nextSrcLine(); // skip ourself
1278 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
1279 setCurSrcLine(ol);
1280 fatal("no ENDMODULE");
1282 if (inum == 0) fatal("no nested modules allowed");
1283 curModule = mi;
1284 piENDMODULE();
1285 return;
1287 } else {
1288 mi = moduleAdd(mn, curSrcLine->fname);
1290 mi->seen = 1;
1291 curModule = mi;
1295 ///////////////////////////////////////////////////////////////////////////////
1296 // DUP, EDUP
1298 static void piDUP (void) {
1299 int defined = 1, dupCnt = 1, inum;
1300 SourceLine *stline, *eline = NULL;
1301 int32_t cnt = getOneExprArg(&defined);
1302 if (!defined) fatal("DUP: counter must be defined");
1303 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
1304 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
1305 // now find corresponding EDUP
1306 // note that we should skip nested DUPs
1307 nextSrcLine(); // skip ourself
1308 stline = curSrcLine;
1309 while (curSrcLine) {
1310 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
1311 // ok, we found something; what is it?
1312 if (inum == 0) {
1313 // new DUP, skip it
1314 ++dupCnt;
1315 nextSrcLine(); // skip DUP
1316 } else {
1317 // EDUP
1318 if (--dupCnt == 0) {
1319 // gotcha!
1320 eline = curSrcLine;
1321 break;
1323 nextSrcLine(); // skip EDUP
1326 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
1327 // now repeat that lines
1328 while (cnt-- > 0) {
1329 setCurSrcLine(stline);
1330 while (curSrcLine != eline) processCurrentLine();
1335 static void piEDUP (void) {
1336 fatal("EDUP withoud DUP");
1340 ///////////////////////////////////////////////////////////////////////////////
1341 // line processor
1343 static void processCurrentLine (void) {
1344 char *str, *ee, name[66];
1345 UrAsmOp *op;
1347 if (!curSrcLine) return; // do nothing
1348 loadCurSrcLine();
1349 //printf("0: [%s]\n", curLine);
1350 processLabel();
1351 //printf("1: [%s]\n", curLine);
1352 // try to find and process command
1353 str = curLine; while (*str && isspace(*str)) ++str; // skip spaces
1354 //printf("2: [%s]\n", str);
1355 // find command end
1356 for (ee = str; *ee && !isspace(*ee) && *ee != '"' && *ee != '\''; ++ee) ;
1357 //printf("3: [%s]\n", ee);
1358 if (ee != str && ee-str <= 64) {
1359 memset(name, 0, sizeof(name));
1360 memmove(name, str, ee-str);
1361 // known command?
1362 op = urFindOp(name);
1363 if (op) {
1364 // ok, do it
1365 str = ee; while (*str && isspace(*str)) ++str; // skip spaces
1366 memmove(curLine, str, strlen(str)+1);
1367 op->fn();
1368 nextSrcLine(); // skip it
1369 return;
1372 // try to compile this
1373 for (;;) {
1374 int len;
1375 const char *errpos;
1376 // skip spaces and ':'
1377 str = curLine; while (*str && (isspace(*str) || *str == ':')) ++str;
1378 memmove(curLine, str, strlen(str)+1);
1379 if (!curLine[0]) break;
1381 len = urAssembleOne(curLine, pc, disp, &errpos);
1382 if (len < 0) fatalUrLib(len);
1383 pc += len; disp += len;
1384 if (len >= 0 && errpos) {
1385 memmove(curLine, errpos+1, strlen(errpos));
1386 } else {
1387 break;
1393 ///////////////////////////////////////////////////////////////////////////////
1394 // setup instructions
1396 static void registerInstructions (void) {
1397 urAddOp("DISPLAY", piDISPLAY);
1398 urAddOp("DISPLAY0", piDISPLAY0);
1399 urAddOp("DISPLAYA", piDISPLAYA);
1401 urAddOp("ORG", piORG);
1402 urAddOp("DISP", piDISP);
1403 urAddOp("ENDDISP", piENDDISP);
1404 urAddOp("ALIGN", piALIGN);
1405 urAddOp("DISPALIGN", piDISPALIGN);
1406 urAddOp("ENT", piENT);
1408 urAddOp("INCBIN", piINCBIN);
1410 urAddOp("MODULE", piMODULE);
1411 urAddOp("ENDMODULE", piENDMODULE);
1413 urAddOp("DUP", piDUP);
1414 urAddOp("EDUP", piEDUP);
1416 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
1417 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
1418 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
1419 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
1420 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
1421 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
1422 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
1423 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
1428 ///////////////////////////////////////////////////////////////////////////////
1429 // preparing another pass
1431 static void initPass (void) {
1432 curSrcLine = asmText;
1433 curModule = NULL;
1434 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
1435 inTapeBlock = 0;
1436 tapeXorB = 0;
1437 wasOrg = 0;
1438 modulesResetSeen();
1439 prepareMemory();
1443 static int posstPass (void) {
1444 if (checkLabels()) return -1;
1445 return 0;
1449 ///////////////////////////////////////////////////////////////////////////////
1450 // options
1452 static struct option longOpts[] = {
1453 {"sna", 0, NULL, 's'},
1454 {"autosna", 0, NULL, 'S'},
1455 {"tap", 0, NULL, 't'},
1456 /*{"rawtap", 0, NULL, 'T'},*/
1457 {"raw", 0, NULL, 'r'},
1458 /*{"dmb", 0, NULL, 'b'},*/
1459 {"none", 0, NULL, 'n'},
1460 {"help", 0, NULL, 'h'},
1461 {NULL, 0, NULL, 0}
1465 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
1468 static void usage (const char *pname) {
1469 int f;
1470 /*" -T --rawtap write raw file with '.tap' extension\n"*/
1471 /*" -b --dmb write .dmb file\n"*/
1472 printf(
1473 "usage: %s [options] infile\n"
1474 "default infiles:", pname);
1475 for (f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
1476 printf("\n"
1477 "options:\n"
1478 " -s --sna write 48K .SNA file\n"
1479 " -S --autosna write 48K .SNA file with autostart\n"
1480 " -t --tap write .tap file\n"
1481 " -r --raw write raw file(s)\n"
1482 " -n --none write nothing\n"
1483 " -h --help this help\n");
1487 static int optWriteType = 'r';
1490 ///////////////////////////////////////////////////////////////////////////////
1491 // main
1493 int main (int argc, char *argv[]) {
1494 int res = 0, c;
1495 const char *pname = argv[0];
1496 char *inFile = NULL;
1497 initInclideDir();
1499 urGetByte = getByte;
1500 urPutByte = putByte;
1501 urFindLabelByName = findLabelCB;
1503 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
1504 while ((c = getopt_long(argc, argv, "sStrnh", longOpts, NULL)) >= 0) {
1505 switch (c) {
1506 case 'S': optRunSNA = 1; optWriteType = 's'; break;
1507 case 's': case 't': case 'r': case 'n': optRunSNA = 0; optWriteType = c; break;
1508 case 'h': usage(pname); res = 0; goto earlyerrquit;
1511 if (optind >= argc) {
1512 // try to find default input file
1513 int f;
1514 for (f = 0; defInFiles[f]; ++f) {
1515 if (!access(defInFiles[f], R_OK)) {
1516 inFile = strdup(defInFiles[f]);
1517 break;
1520 } else {
1521 inFile = strdup(argv[optind]);
1523 if (!inFile || !inFile[0]) {
1524 res = 1;
1525 fprintf(stderr, "ERROR: no input file!\n");
1526 goto earlyerrquit;
1529 registerInstructions();
1531 res = asmTextLoad(argc>1?argv[1]:"ztest.saz");
1532 if (!res) {
1534 printf("dumping...\n");
1535 FILE *fo = fopen("z000.out", "w");
1536 if (fo) {
1537 SourceLine *c;
1538 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
1539 fclose(fo);
1542 for (pass = 0; pass <= 1; ++pass) {
1543 initPass();
1544 printf("pass #%d\n", pass);
1545 setCurSrcLine(asmText);
1546 if (setjmp(errJP)) { res = 1; break; }
1547 while (curSrcLine) processCurrentLine();
1548 if (posstPass()) { res = 1; break; }
1550 // write result
1552 char *oc = strdup(inFile);
1553 char *pd = strrchr(oc, '.');
1554 if (pd && !strchr(oc, '/')) *pd = '\0';
1555 switch (optWriteType) {
1556 case 's': saveSna(oc); break;
1557 case 't': saveTap(oc); break;
1558 case 'r': saveRaw(oc); break;
1560 free(oc);
1562 } else {
1563 fprintf(stderr, "ERROR: loading error!\n");
1566 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
1567 urClearLabels();
1568 modulesClear();
1569 asmTextClear();
1570 urClearOps();
1571 earlyerrquit:
1572 if (inFile) free(inFile);
1573 if (sysIncludeDir) free(sysIncludeDir);
1574 return res?1:0;