DMB1 writer; some fixes to 'allo' sample
[urasm.git] / src / urasm.c
blob59d93ce16b7737da4650c58d3c581a731905fd17
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 <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
19 #include "liburasm/liburasm.h"
20 #include "ursna48.h"
23 #define VERSION_HI 0
24 #define VERSION_MID 1
25 #define VERSION_LO 0
28 #define MAYBE_UNUSED __attribute__((unused))
30 ///////////////////////////////////////////////////////////////////////////////
31 // global variables
33 static char *sysIncludeDir = NULL;
35 #define MAX_LINE_SIZE 16384
36 static char curLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
39 ///////////////////////////////////////////////////////////////////////////////
40 // init dirs
42 static void initInclideDir (void) {
43 const char *id = getenv("URASM_INCLUDE_DIR");
44 if (id && id[0]) {
45 sysIncludeDir = strdup(id);
46 } else {
47 char myDir[4096];
48 memset(myDir, 0, sizeof(myDir));
49 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) strcpy(myDir, ".");
50 else {
51 char *p = (char *)strrchr(myDir, '/');
52 if (!p) strcpy(myDir, "."); else *p = '\0';
54 strcat(myDir, "/libs");
55 sysIncludeDir = strdup(myDir);
57 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
58 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
62 ///////////////////////////////////////////////////////////////////////////////
63 // string utilities
65 /*TODO: expression parser should understant escapes in strings!*/
66 /* trim trailing spaces and comments */
67 static void normalizeStr (char *s) {
68 char *p, inQ = 0;
69 int f;
70 // now skip all shit
71 for (p = s; *p; ++p) {
72 if (inQ) {
73 // inside the string
74 if (*p == '\\') {
75 switch (*(++p)) {
76 case 'x': case 'X': // hex code
77 ++p; // skip 'x'
78 for (f = 0; f < 2; ++f) if (!isxdigit(*p)) break;
79 --p; // last digit will be skiped by 'for'
80 break;
81 case '0': // octal code
82 for (f = 0; f < 4; ++f) if (!isdigit(*p) || *p > '7') break;
83 --p; // last digit will be skiped by 'for'
84 break;
85 case '1' ... '9': // decimal code
86 for (f = 0; f < 3; ++f) if (!isdigit(*p)) break;
87 --p; // last digit will be skiped by 'for'
88 break;
89 default: ; // other escapes, do nothing
91 } else {
92 if (*p == inQ) inQ = 0;
94 continue;
96 // outside the string
97 switch (*p) {
98 case '"': case '\'': // string catched
99 inQ = *p;
100 break;
101 case ';': // end of line, comment follows
102 *p-- = '\0'; // strip it and quit
103 break;
104 default: ; // do nothing
107 // trim right
108 for (p = s+strlen(s)-1; p >= s && isspace(*p); --p) ;
109 p[1] = '\0';
113 /* returns NULL or pointer to args */
114 /* skips spaces after command if any */
115 static char *strIsCommand (const char *command, char *str) {
116 int cnt;
118 for (cnt = 1; cnt > 0; --cnt) {
119 while (*str && isspace(*str)) ++str; // skip spaces
120 for (; *command && *str; ++command, ++str) {
121 if (toupper(*command) != toupper(*str)) return NULL; // alas
123 if (*command) return NULL; // alas
124 if (*str && isalnum(*str)) return NULL; // alas
125 while (*str && isspace(*str)) ++str; // skip spaces
126 if (*str && *str == ':') break; // try again if we have a colon
127 return str; // found
129 return NULL;
133 /* don't free() result */
134 /* skips trailing spaces */
135 static char *parseStr (char **str, char endQ, int *lenp) {
136 static char buf[MAX_LINE_SIZE];
137 int len = 0, n, f, base;
138 char *a = *str;
140 int xDigit (char ch, int base) {
141 if (ch < '0') return -1;
142 if (base <= 10) {
143 if (ch >= '0'+base) return -1;
144 return ch-'0';
146 ch = toupper(ch);
147 if (ch <= '9') return ch-'0';
148 if (ch < 'A' || ch > 'A'+base-10) return -1;
149 ch -= 'A'-10;
150 return ch<base ? ch : -1;
153 memset(buf, 0, sizeof(buf));
154 if (lenp) *lenp = 0;
155 for (; *a; ++a) {
156 if (*a == '\\') {
157 if (!a[1]) break;
158 switch (*(++a)) {
159 case 'a': buf[len++] = '\a'; break;
160 case 'b': buf[len++] = '\b'; break;
161 case 'f': buf[len++] = '\f'; break;
162 case 'n': buf[len++] = '\n'; break;
163 case 'r': buf[len++] = '\r'; break;
164 case 't': buf[len++] = '\t'; break;
165 case 'v': buf[len++] = '\v'; break;
166 case 'z': buf[len++] = '\0'; break;
167 case 'x': case 'X': // hex
168 ++a; // skip 'x'
169 base = 16; f = 2;
170 donum: for (n = 0; f > 0; --f) {
171 char ch = xDigit(*a++, base);
172 if (ch < 0) { --a; break; }
173 n *= base;
174 n += ch;
176 buf[len++] = n;
177 --a; // return to the last digit, 'for' will skip it
178 break;
179 case '0': // octal
180 base = 8; f = 4;
181 goto donum;
182 case '1' ... '9': // decimal
183 base = 10; f = 3;
184 goto donum;
185 default: buf[len++] = a[0]; break; // others
187 } else {
188 if (*a == endQ) { ++a; break; }
189 buf[len++] = *a;
192 while (*a && isspace(*a)) ++a; // skip trailing spaces
193 *str = a;
194 buf[len] = '\0';
195 if (lenp) *lenp = len;
196 return buf;
200 ///////////////////////////////////////////////////////////////////////////////
201 // source file stack, reader, etc
203 typedef struct SourceLine SourceLine;
204 struct SourceLine {
205 SourceLine *next;
206 char *line;
207 char *fname;
208 int lineNo;
211 static SourceLine *asmText = NULL;
212 static SourceLine *asmTextLast = NULL;
213 static SourceLine *curSrcLine = NULL;
216 static void asmTextClear (void) {
217 while (asmText) {
218 SourceLine *l = asmText;
220 asmText = asmText->next;
221 free(l->line);
222 free(l->fname);
223 free(l);
225 asmTextLast = curSrcLine = NULL;
229 static void normIncName (char *dest, const char *fn, int system) {
230 struct stat st;
232 if (system) sprintf(dest, "%s/%s", sysIncludeDir, fn); else sprintf(dest, "%s", fn);
233 if (stat(dest, &st)) return;
234 if (S_ISDIR(st.st_mode)) strcat(dest, "/zzmain.zas");
238 static int asmTextLoad (const char *fname) {
239 FILE *fl;
240 int lineNo = 0;
241 char *args;
242 SourceLine *s;
243 static int includeCount = 0;
245 if (!(fl = fopen(fname, "r"))) {
246 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
247 return -1;
249 printf("loading: %s\n", fname);
250 // read file
251 while (fgets(curLine, sizeof(curLine)-1, fl)) {
252 ++lineNo;
253 curLine[sizeof(curLine)-1] = '\0';
254 normalizeStr(curLine);
255 if (!curLine[0]) continue; // don't store empty lines
256 // find specials, if any
257 if (isspace(curLine[0])) {
258 if ((args = strIsCommand("INCLUDE", curLine)) != NULL) {
259 // process 'INCLUDE'
260 int system = 0;
261 char qCh = 0;
263 if (!args[0]) {
264 fclose(fl);
265 fprintf(stderr, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname, lineNo);
266 return -1;
268 if (args[0] == '"' || args[0] == '\'') qCh = *args++;
269 else if (args[0] == '<') { qCh = '>'; ++args; system = 1; }
270 char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
271 if (!fn[0]) {
272 fclose(fl);
273 fprintf(stderr, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname, lineNo);
274 return -1;
276 if (args[0]) {
277 fclose(fl);
278 fprintf(stderr, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname, lineNo);
279 return -1;
281 if (includeCount > 256) {
282 fclose(fl);
283 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname, lineNo);
284 return -1;
286 normIncName(curLine, fn, system);
287 //if (system) sprintf(curLine, "%s/%s", sysIncludeDir, fn); else sprintf(curLine, "%s", fn);
288 fn = strdup(curLine); if (!fn) abort();
289 ++includeCount;
290 system = asmTextLoad(fn);
291 --includeCount;
292 free(fn);
293 if (system) { fclose(fl); return system; }
294 continue;
297 // add current line
298 s = calloc(1, sizeof(SourceLine));
299 if (!s) abort();
300 s->lineNo = lineNo;
301 s->line = strdup(curLine); if (!s->line) abort();
302 s->fname = strdup(fname); if (!s->fname) abort();
303 if (asmTextLast) asmTextLast->next = s; else asmText = s;
304 asmTextLast = s;
306 fclose(fl);
307 return 0;
311 static inline void loadCurSrcLine (void) { if (curSrcLine) strcpy(curLine, (curSrcLine != NULL ? curSrcLine->line : "")); }
312 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
313 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
316 ///////////////////////////////////////////////////////////////////////////////
317 // prototypes
319 static void processCurrentLine (void); // only one, will skip to next one
322 ///////////////////////////////////////////////////////////////////////////////
323 // error raisers, etc
325 static jmp_buf errJP;
328 static void errorWriteFile (void) {
329 if (curSrcLine) {
330 fprintf(stderr, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
331 } else {
332 fprintf(stderr, "somewhere in time: ");
336 static void errorMsgV (const char *fmt, va_list ap) {
337 errorWriteFile();
338 vfprintf(stderr, fmt, ap);
339 va_end(ap);
340 fputc('\n', stderr);
341 fflush(stderr);
345 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
346 va_list ap;
347 fprintf(stderr, "WARNING ");
348 va_start(ap, fmt);
349 errorMsgV(fmt, ap);
353 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
354 va_list ap;
355 fprintf(stderr, "FATAL ");
356 va_start(ap, fmt);
357 errorMsgV(fmt, ap);
361 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
362 va_list ap;
363 va_start(ap, fmt);
364 errorMsgV(fmt, ap);
365 longjmp(errJP, 666);
369 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
370 errorMsg("%s", urErrorMessage(errcode));
371 longjmp(errJP, 666);
375 //////////////////////////////////////////////////////////////////////////////
376 // operator management
378 typedef int (*UrAsmOpFn) (void);
380 typedef struct UrAsmOp {
381 char *name;
382 UrAsmOpFn fn;
383 struct UrAsmOp *next;
384 } UrAsmOp;
386 static UrAsmOp *oplist = NULL;
389 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
390 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
391 if (!res) abort();
392 res->name = strdup(name);
393 res->fn = fn;
394 res->next = oplist;
395 oplist = res;
396 return res;
400 static UrAsmOp *urFindOp (const char *name) {
401 UrAsmOp *res;
402 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
403 return res;
407 static void urClearOps (void) {
408 UrAsmOp *c;
409 while (oplist) {
410 c = oplist; oplist = oplist->next;
411 free(c->name);
412 free(c);
417 ///////////////////////////////////////////////////////////////////////////////
418 // label management
420 typedef struct UrLabelInfo UrLabelInfo;
421 struct UrLabelInfo {
422 char *name;
423 int32_t value;
424 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
425 int known; /* !0: label value already known */
426 int refLine; /* first referenced line */
427 char *refFile;
428 UrLabelInfo *next;
431 static UrLabelInfo *labels = NULL;
433 static void urClearLabels (void) {
434 UrLabelInfo *c;
435 while ((c = labels)) {
436 labels = labels->next;
437 if (c->name) free(c->name);
438 if (c->refFile) free(c->refFile);
439 free(c);
444 static UrLabelInfo *urFindLabel (const char *name) {
445 UrLabelInfo *c;
446 for (c = labels; c; c = c->next) if (!strcmp(name, c->name)) break;
447 return c;
451 static UrLabelInfo *urAddLabel (const char *name) {
452 UrLabelInfo *c = urFindLabel(name);
453 if (!c) {
454 UrLabelInfo *p;
455 for (p = NULL, c = labels; c; p = c, c = c->next) ;
456 c = calloc(1, sizeof(UrLabelInfo));
457 if (!c) abort();
458 c->name = strdup(name);
459 c->type = -1;
460 if (p) p->next = c; else labels = c;
461 c->next = NULL;
463 return c;
467 ///////////////////////////////////////////////////////////////////////////////
468 // module list management
470 typedef struct ModuleInfo ModuleInfo;
471 struct ModuleInfo {
472 char *name;
473 char *fname; // opened in this file
474 int seen; // !0: module already seen, skip other definitions from the same file
475 ModuleInfo *next;
477 static ModuleInfo *modules = NULL;
478 static ModuleInfo *curModule = NULL;
481 static void modulesClear (void) {
482 curModule = NULL;
483 while (modules) {
484 ModuleInfo *c = modules;
485 modules = modules->next;
486 free(c->name);
487 free(c->fname);
488 free(c);
493 static void modulesResetSeen (void) {
494 ModuleInfo *c;
495 for (c = modules; c; c = c->next) c->seen = 0;
499 static ModuleInfo *moduleFind (const char *name) {
500 ModuleInfo *c;
501 if (!name || !name[0]) return NULL;
502 for (c = modules; c; c = c->next) if (!strcmp(c->name, name)) break;
503 return c;
507 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
508 ModuleInfo *c;
509 if (!name || !fname || !name[0] || !fname[0]) abort();
510 c = calloc(1, sizeof(ModuleInfo));
511 if (!c) abort();
512 c->name = strdup(name); if (!c->name) abort();
513 c->fname = strdup(fname); if (!c->fname) abort();
514 c->next = modules;
515 modules = c;
516 return c;
520 ///////////////////////////////////////////////////////////////////////////////
521 // destination memory management
523 static uint8_t memory[65536];
524 static char memused[65536];
525 static char memresv[65536];
526 static uint16_t pc = 0; /* current position to write */
527 static uint16_t disp = 0; /* current 'virtual PC' */
528 static uint16_t ent = 0; /* starting address */
529 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
530 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
531 static int inTapeBlock = 0;
532 static uint8_t tapeXorB = 0;
535 static inline uint8_t getByte (uint16_t addr) {
536 return memory[addr];
540 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
541 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
545 static inline void putByte (uint16_t addr, uint8_t b) {
546 if (inTapeBlock) tapeXorB ^= b;
547 memory[addr] = b;
548 memused[addr] = 1;
552 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
553 putByte(addr, w&0xFFU);
554 putByte(addr+1, (w>>8)&0xFFU);
558 static inline void emitByte (uint8_t b) {
559 putByte(pc, b);
560 ++pc;
561 ++disp;
565 static inline void emitWord (uint16_t w) {
566 emitByte(w&0xFFU);
567 emitByte((w>>8)&0xFFU);
571 static inline void emitRWord (uint16_t w) {
572 emitByte((w>>8)&0xFFU);
573 emitByte(w&0xFFU);
577 static void prepareMemory (void) {
578 memset(memory, 0, sizeof(memory));
579 memset(memused, 0, sizeof(memused));
580 memset(memresv, 0, sizeof(memresv));
584 ///////////////////////////////////////////////////////////////////////////////
585 // label getter and utilities
587 static char *lastSeenGlobalLabel = NULL; /* global */
590 static char *fixLocalLabel (const char *name) {
591 static char newname[MAX_LINE_SIZE*2+1024];
593 memset(newname, 0, sizeof(newname));
594 if (!name || !name[0]) newname[0] = '\0';
595 else if (!lastSeenGlobalLabel || name[0] != '.') strcpy(newname, name);
596 else {
597 // this is local label, let's rename it
598 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
600 return newname;
604 static char *fixGlobalLabel (const char *name) {
605 static char newname[MAX_LINE_SIZE*2+1024];
607 memset(newname, 0, sizeof(newname));
608 if (!name || !name[0]) newname[0] = '\0';
609 else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
610 if (name[0] == '@' && name[1]) ++name;
611 strcpy(newname, name);
612 } else {
613 // this is global unqualified label and we have a module; let's rename it
614 sprintf(newname, "%s.%s", curModule->name, name);
616 //printf("%s --> %s\n", name, newname);
617 return newname;
621 static int lblOptMakeU2 = 0;
622 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found) {
623 UrLabelInfo *lbl;
624 char *ln, *nn;
626 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
627 lbl = urFindLabel(nn);
628 if (!lbl) {
629 // try non-module label
630 lbl = urFindLabel(ln);
632 if (!lbl) {
633 if (pass != 0) {
634 errorMsg("using undefined label %s", ln);
635 *found = 0;
636 *defined = 0;
637 return 0;
639 lbl = urAddLabel(nn);
640 lbl->type = lblOptMakeU2 ? -42 : -1;
641 lbl->known = 0;
642 lbl->refLine = curSrcLine->lineNo;
643 lbl->refFile = strdup(curSrcLine->fname);
644 //printf("new label: [%s]\n", lbl->name);
645 } else {
646 //printf("label reference: [%s]\n", lbl->name);
648 if (lbl) {
649 *found = 1;
650 *defined = lbl->known!=0;
651 return lbl->value;
653 *found = 0;
654 *defined = 0;
655 return 0;
659 // qtypes
660 enum {
661 UR_QTYPE_DEFINED,
662 UR_QTYPE_KNOWN
666 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
667 UrLabelInfo *lbl;
668 char *ln, *nn;
670 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
671 lbl = urFindLabel(nn);
672 if (!lbl) {
673 // try non-module label
674 lbl = urFindLabel(ln);
676 switch (qtype) {
677 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
678 case UR_QTYPE_KNOWN: return lbl!=NULL;
679 default: ;
681 return 0;
685 static int checkLabels (void) {
686 UrLabelInfo *c;
687 int wasError = 0;
689 for (c = labels; c; c = c->next) {
690 if (c->type == -1) {
691 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
692 wasError = 1;
694 if (c->type == 0) c->known = -1;
696 //if (wasError) longjmp(errJP, 667);
697 return wasError;
701 ///////////////////////////////////////////////////////////////////////////////
702 // expression utils
704 static int32_t getExprArg (int *defined) {
705 int error = 0;
706 char *a = curLine;
707 const char *ee;
708 int32_t res;
710 while (*a && isspace(*a)) ++a;
711 if (!a[0]) fatal("expression expected");
712 ee = urExpression(&res, a, disp, defined, &error);
713 if (error) fatalUrLib(error);
714 if (*ee) {
715 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
716 memmove(curLine, ee, strlen(ee)+1);
717 } else {
718 curLine[0] = '\0';
720 return res;
724 static int32_t getOneExprArg (int *defined) {
725 int32_t res = getExprArg(defined);
727 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
728 return res;
732 static int isStrArg (void) { return (curLine[0] == '"' || curLine[0] == '\''); }
735 static char *getStrArg (int *lenp) {
736 char *res, qCh;
737 char *a = curLine;
739 while (*a && isspace(*a)) ++a;
740 qCh = *a++;
741 if (qCh != '"' && qCh != '\'') fatal("string expected");
742 res = parseStr(&a, qCh, lenp);
743 if (*a) {
744 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
745 memmove(curLine, a, strlen(a)+1);
746 } else {
747 curLine[0] = '\0';
749 return res;
753 static MAYBE_UNUSED char *getOneStrArg (int *lenp) {
754 char *res = getStrArg(lenp);
756 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
757 return res;
761 static char *getLabelArg (void) {
762 static char res[MAX_LINE_SIZE+128], *p;
763 char *a = curLine;
765 memset(res, 0, sizeof(res));
766 while (*a && isspace(*a)) ++a;
767 if (!a[0]) fatal("label expected");
768 for (p = res; *a && *a != ','; ++a, ++p) *p = *a;
769 for (; p > res && isspace(p[-1]); --p) ;
770 *p = '\0';
771 if (p-res > 120) fatal("label name too long: %s", res);
772 while (*a && isspace(*a)) ++a;
773 if (*a) {
774 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
775 memmove(curLine, a, strlen(a)+1);
776 } else {
777 curLine[0] = '\0';
779 return res;
783 static char *getOneLabelArg (void) {
784 char *res = getLabelArg();
786 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
787 return res;
791 /* res == 0: end of expression */
792 static int skipComma (void) {
793 char *a;
795 for (a = curLine; *a && isspace(*a); ++a) ;
796 if (!a[0]) { curLine[0] = '\0'; return 0; }
797 if (a[0] == ':') {
798 while (a[0] && (a[0] == ':' || isspace(a[0]))) ++a;
799 if (!a[0]) { curLine[0] = '\0'; return 0; }
800 memmove(curLine, a, strlen(a)+1);
801 return -1;
803 if (a[0] != ',') fatal("invalid expression: ',' expected");
804 for (++a; *a && isspace(*a); ++a) ;
805 if (!a[0]) { curLine[0] = '\0'; return 0; }
806 memmove(curLine, a, strlen(a)+1);
807 return 1;
811 static void skipInstruction (void) {
812 char *str = curLine;
813 int cnt;
815 for (cnt = 1; cnt > 0; --cnt) {
816 while (*str && isspace(*str)) ++str; // skip spaces
817 while (*str && !isspace(*str)) ++str; // skip non-spaces
818 while (*str && isspace(*str)) ++str; // skip spaces
819 if (!str[0] || *str != ':') break;
820 // try again if we have a colon
822 memmove(curLine, str, strlen(str)+1);
826 ///////////////////////////////////////////////////////////////////////////////
827 // label processor
829 static MAYBE_UNUSED void removeSpacesAndColons (void) {
830 char *ep = curLine;
832 while (*ep && (isspace(*ep) || *ep == ':')) ++ep;
833 memmove(curLine, ep, strlen(ep)+1);
837 static void checkExprEnd (void) {
838 char *ep = curLine;
840 while (*ep && isspace(*ep)) ++ep;
841 memmove(curLine, ep, strlen(ep)+1);
842 if (curLine[0] && curLine[0] != ':') fatal("end of expression expected");
846 /* remove label from curLine */
847 static void removeLabel (void) {
848 char *ep = curLine;
850 if (ep[0] && !isspace(ep[0])) for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ; // skip text
851 // skip spaces and colons
852 while (*ep && (isspace(*ep) || *ep == ':')) ++ep;
853 memmove(curLine, ep, strlen(ep)+1);
857 static void processLabel (void) {
858 char *argstart;
859 char *ep, *ln, *nn;
860 static char n2[256];
861 UrLabelInfo *lbl;
862 int noLocAff = 0, doEQU = 0;
864 memset(n2, 0, sizeof(n2));
865 if (!curLine[0] || isspace(curLine[0]) || curLine[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
866 // collect label
867 for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ;
868 if (ep-curLine > 120) fatal("label too long");
869 // copy label
870 memset(n2, 0, sizeof(n2));
871 memmove(n2, curLine, ep-curLine);
872 if (!urIsValidLabelName(n2)) return; // this is not a valid label, get out of here
873 // check if this can be instruction
874 while (*ep && isspace(*ep)) ++ep;
875 if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
876 // ok, we got a good label
877 removeLabel();
878 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
879 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
880 lbl = urAddLabel(nn);
881 if (!lbl->refFile) {
882 lbl->refLine = curSrcLine->lineNo;
883 lbl->refFile = strdup(curSrcLine->fname);
885 //printf("new: [%s]\n", lbl->name);
886 // get command name
887 if (curLine[0] == '=') {
888 doEQU = 0;
889 argstart = curLine+1;
890 } else {
891 doEQU = 1;
892 argstart = strIsCommand("EQU", curLine);
894 if (!argstart || doEQU) {
895 if (pass == 0 && lbl->type != -1) fatal("duplicate label %s", lbl->name);
897 if (argstart) {
898 // do '=' or 'EQU'
899 memmove(curLine, argstart, strlen(argstart)+1);
900 if (!doEQU) {
901 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label %s", lbl->name);
903 int defined = 1;
904 int32_t res = getOneExprArg(&defined);
905 lbl->type = doEQU;
906 if (defined) {
907 lbl->value = res;
908 lbl->known = 1;
909 } else {
910 if (pass != 0) fatal("can't calculate label %s", lbl->name);
912 curLine[0] = '\0';
913 return;
915 // code label
916 if (lbl->name[0] != '{' && !noLocAff) {
917 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
918 lastSeenGlobalLabel = strdup(lbl->name);
920 lbl->type = 2;
921 lbl->value = disp;
922 lbl->known = 1;
926 ///////////////////////////////////////////////////////////////////////////////
927 // instruction finder (in source)
929 /* array ends with NULL */
930 /* returns line or NULL */
931 /* iidx will be set to found instruction number */
932 static SourceLine *findNextInstructionFromList (int *iidx, ...) {
933 if (iidx) *iidx = -1;
934 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
935 int f;
936 va_list ap;
938 //if (!isspace(cur->line[0])) continue; // fuck labeled strings
939 va_start(ap, iidx);
940 for (f = 0;;++f) {
941 const char *name = va_arg(ap, const char *);
943 if (!name) break;
944 if (strIsCommand(name, cur->line)) {
945 va_end(ap);
946 if (iidx) *iidx = f;
947 return cur;
950 va_end(ap);
952 return NULL;
956 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
957 return findNextInstructionFromList(NULL, name, NULL);
961 ///////////////////////////////////////////////////////////////////////////////
962 // writers
964 static int optRunSNA = 1;
965 static int optRunTape = 1;
966 static int optRunDMB = 1;
967 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
970 /* return 'found' flag */
971 static int findChunkFrom (int addr, int *start, int *len) {
972 if (addr < 0) addr = 0;
973 for (; addr <= 65535 && !memused[addr]; ++addr) ;
974 if (addr > 65535) return 0;
975 *start = addr;
976 for (; addr <= 65535 && memused[addr]; ++addr) ;
977 *len = addr-(*start);
978 return 1;
982 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
983 for (; buflen >= 2; buflen -= 2) {
984 int cnt = *buf++;
985 uint8_t byte = *buf++;
986 //printf("%d %u %u\n", cnt+1, byte, addr);
987 for (; cnt >= 0; --cnt, ++addr) if (!memused[addr]) putByte(addr, byte);
992 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
993 if (fwrite(&b, 1, 1, fo) != 1) return -1;
994 if (bxor) *bxor = (*bxor)^b;
995 return 0;
999 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1000 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1001 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1002 return 0;
1006 ///////////////////////////////////////////////////////////////////////////////
1007 // .sna
1009 static int saveSna (const char *fname) {
1010 char *fn = malloc(strlen(fname)+16);
1011 uint8_t regs[27];
1012 FILE *fo;
1014 sprintf(fn, "%s.sna", fname);
1015 fo = fopen(fn, "wb");
1016 free(fn);
1017 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1018 printf("out: %s.sna\n", fname);
1019 memmove(regs, ursna48, 27);
1020 if (optRunSNA) {
1021 /* push new address */
1022 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1023 sp -= 2;
1024 putByte(sp, ent&0xFFU);
1025 putByte(sp+1, (ent>>8)&0xFFU);
1026 regs[23] = sp&0xFFU;
1027 regs[24] = (sp>>8)&0xFFU;
1029 fwrite(regs, 27, 1, fo);
1030 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1031 fwrite(memory+16384, 49152, 1, fo);
1032 fclose(fo);
1033 return 0;
1037 ///////////////////////////////////////////////////////////////////////////////
1038 // bin chunks
1040 static void saveRaw (const char *basename) {
1041 char *fname = malloc(strlen(basename)+16);
1042 int start = 0, len;
1043 FILE *fo;
1045 while (findChunkFrom(start, &start, &len)) {
1046 sprintf(fname, "%s_%04X.%s", basename, start, optTapExt?"tap":"bin");
1047 fo = fopen(fname, "wb");
1048 if (!fo) {
1049 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1050 } else {
1051 printf("out: %s\n", fname);
1052 if (fwrite(memory+start, len, 1, fo) != 1) {
1053 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1054 fclose(fo);
1055 unlink(fname);
1056 } else {
1057 fclose(fo);
1060 start += len;
1062 free(fname);
1066 ///////////////////////////////////////////////////////////////////////////////
1067 // .dmb
1069 static int fwriteW16 (FILE *fo, uint16_t w) {
1070 uint8_t b = w&0xff;
1072 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1073 b = (w>>8)&0xff;
1074 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1075 return 0;
1079 static int saveDMB (const char *fname) {
1080 char *fn = malloc(strlen(fname)+16);
1081 int start = 0, len;
1082 uint16_t pcnt = 0;
1083 FILE *fo;
1085 sprintf(fn, "%s.dmb", fname);
1086 fo = fopen(fn, "wb");
1087 free(fn);
1088 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
1090 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
1092 printf("out: %s.dmb\n", fname);
1093 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
1094 if (fwriteW16(fo, (optRunDMB ? ent : 0)) != 0) goto error;
1095 if (fwriteW16(fo, (optRunDMB ? clrAddr : 0)) != 0) goto error;
1096 if (fwriteW16(fo, pcnt) != 0) goto error;
1098 start = 0;
1099 while (findChunkFrom(start, &start, &len)) {
1100 if (fwriteW16(fo, len) != 0) goto error;
1101 if (fwriteW16(fo, start) != 0) goto error;
1102 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
1103 start += len;
1105 fclose(fo);
1106 return 0;
1107 error:
1108 fclose(fo);
1109 unlink(fname);
1110 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
1111 return -1;
1115 ///////////////////////////////////////////////////////////////////////////////
1116 // .tap
1118 static char tapeLoaderName[16];
1121 static void saveTapCargador (FILE *fo) {
1122 // count blocks
1123 static uint8_t cargador[16384]; // should be enough for everyone
1124 int start = 0, len, pos, f;
1125 uint8_t bxor;
1128 void putStr (const char *s) {
1129 for (; *s; ++s) cargador[pos++] = *s;
1132 void putNum (int num) {
1133 char buf[64];
1134 sprintf(buf, "%d", num);
1135 putStr(buf);
1139 pos = 4;
1140 // number
1141 cargador[0] = 0; cargador[1] = 10;
1142 // size (will be fixed later)
1143 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
1144 // load blocks
1145 while (findChunkFrom(start, &start, &len)) {
1146 // :LOAD "" CODE
1147 putStr(":\xef\"\"\xaf");
1148 start += len;
1150 // and run
1151 // :RANDOMIZE USR VAL "xxx"
1152 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
1153 // patch len
1154 cargador[2] = (pos-4)&0xff;
1155 cargador[3] = ((pos-4)>>8)&0xff;
1156 // write header
1157 fWriteWord(fo, 19, NULL); // length of header
1158 bxor = 0;
1159 fWriteByte(fo, 0, &bxor); // header block
1160 fWriteByte(fo, 0, &bxor); // 'basic' flag
1161 if (tapeLoaderName[0]) {
1162 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1163 } else {
1164 fWriteByte(fo, 'c', &bxor);
1165 fWriteByte(fo, 'a', &bxor);
1166 fWriteByte(fo, 'r', &bxor);
1167 fWriteByte(fo, 'g', &bxor);
1168 fWriteByte(fo, 'a', &bxor);
1169 fWriteByte(fo, 'd', &bxor);
1170 fWriteByte(fo, 'o', &bxor);
1171 fWriteByte(fo, 'r', &bxor);
1172 fWriteByte(fo, ' ', &bxor);
1173 fWriteByte(fo, ' ', &bxor);
1175 fWriteWord(fo, pos, &bxor); // length
1176 fWriteWord(fo, 10, &bxor); // start
1177 fWriteWord(fo, pos, &bxor); // length2
1178 fWriteByte(fo, bxor, NULL);
1179 // write data
1180 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
1181 bxor = 0;
1182 fWriteByte(fo, 0xFFU, &bxor); // data block
1183 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
1184 fWriteByte(fo, bxor, NULL);
1188 static void saveTap (const char *basename) {
1189 char *fname = malloc(strlen(basename)+16);
1190 char blkname[128];
1191 int start = 0, len, f;
1192 uint8_t bxor;
1193 FILE *fo;
1195 sprintf(fname, "%s.tap", basename);
1196 fo = fopen(fname, "wb");
1197 free(fname);
1198 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
1199 printf("out: %s.tap\n", basename);
1200 if (optRunTape) saveTapCargador(fo);
1201 while (findChunkFrom(start, &start, &len)) {
1202 // write header
1203 if (tapeLoaderName[0]) {
1204 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1205 memcpy(blkname, tapeLoaderName, 10);
1206 } else {
1207 sprintf(blkname, "c%04X:%04X", start, len);
1209 //printf(" block: %s\n", blkname);
1210 fWriteWord(fo, 19, NULL); // length of header
1211 bxor = 0;
1212 fWriteByte(fo, 0, &bxor); // header block
1213 fWriteByte(fo, 3, &bxor); // 'code' flag
1214 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
1215 fWriteWord(fo, len, &bxor);
1216 fWriteWord(fo, start, &bxor);
1217 fWriteWord(fo, 32768, &bxor);
1218 fWriteByte(fo, bxor, NULL);
1219 // write data
1220 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
1221 bxor = 0;
1222 fWriteByte(fo, 0xFFU, &bxor); // data block
1223 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
1224 fWriteByte(fo, bxor, NULL);
1225 start += len;
1227 fclose(fo);
1231 ///////////////////////////////////////////////////////////////////////////////
1232 // pseudoinstructions
1234 // note that processCurrentLine() will NOT skip to the next line before
1235 // calling pseudoinstruction handler!
1236 // note that processCurrentLine() will skip current line after calling
1237 // pseudoinstruction handler!
1239 static int wasOrg = 0;
1240 static int wasClr = 0;
1243 ///////////////////////////////////////////////////////////////////////////////
1244 // user warnings
1246 static int piDISPLAYX (int passNo, int asHex) {
1247 for (;;) {
1248 if (isStrArg()) {
1249 int len = 0;
1250 char *res = getStrArg(&len);
1252 if (passNo < 0 || pass == passNo) printf("%s", res);
1253 } else {
1254 int defined = 1;
1255 int32_t v = getExprArg(&defined);
1257 if (passNo < 0 || pass == passNo) {
1258 if (asHex) printf("%04X", (unsigned int)v);
1259 else printf("%d", v);
1262 if (!skipComma()) break;
1264 return 0;
1268 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
1269 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
1270 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
1271 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
1272 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
1273 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
1276 ///////////////////////////////////////////////////////////////////////////////
1277 // ORG, DISP, etc.
1279 static int piORG (void) {
1280 int defined = 1;
1281 int32_t res = getOneExprArg(&defined);
1283 if (!defined) fatal("sorry, ORG operand value must be known here");
1284 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
1285 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
1286 pc = disp = res;
1287 if (!wasOrg) {
1288 wasOrg = 1;
1289 ent = res;
1290 if (!wasClr && res > 0) clrAddr = res-1;
1292 return 0;
1296 static int piDISP (void) {
1297 int defined = 1;
1298 int32_t res = getOneExprArg(&defined);
1300 if (!defined) fatal("sorry, DISP operand value must be known here");
1301 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
1302 //printf("DISP=%d\n", res);
1303 disp = res;
1304 return 0;
1308 static int piENDDISP (void) {
1309 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1310 checkExprEnd();
1311 disp = pc;
1312 return 0;
1316 static int piENT (void) {
1317 int defined = 1;
1318 int32_t res = getOneExprArg(&defined);
1320 //if (!defined) fatal("sorry, ENT operand value must be known here");
1321 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
1322 ent = res;
1323 return 0;
1327 static int piCLR (void) {
1328 int defined = 1;
1329 int32_t res = getOneExprArg(&defined);
1331 //if (!defined) fatal("sorry, CLR operand value must be known here");
1332 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
1333 clrAddr = res;
1334 wasClr = 1;
1335 return 0;
1339 static int piRESERVE (void) {
1341 int defined = 1, start;
1342 int32_t res = getExprArg(&defined);
1343 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1344 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1345 start = res;
1346 if (!skipComma()) fatal("RESERVE needs 2 args");
1347 res = getOneExprArg(&defined);
1348 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1349 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1350 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
1352 fatal("RESERVE: not yet!");
1353 return 0;
1357 static int piALIGN (void) {
1358 int defined = 1;
1359 int32_t res;
1361 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1362 res = getOneExprArg(&defined);
1363 if (!defined) fatal("sorry, ALIGN operand value must be known here");
1364 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
1365 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
1366 if (res > 0 && pc%res != 0) {
1367 pc /= res;
1368 pc *= res;
1369 pc += res;
1370 disp = pc;
1372 return 0;
1376 static int piDISPALIGN (void) {
1377 int defined = 1;
1378 int32_t res = getOneExprArg(&defined);
1380 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
1381 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
1382 if (res > 0 && disp%res != 0) {
1383 disp /= res;
1384 disp *= res;
1385 disp += res;
1387 return 0;
1391 ///////////////////////////////////////////////////////////////////////////////
1392 // DEFx
1394 static int defIncr = 0;
1397 static int piDEFINCR (void) {
1398 int defined = 1;
1399 int32_t cnt = getOneExprArg(&defined);
1401 if (!defined) fatal("DEFINCR: increment must be defined");
1402 defIncr = cnt;
1403 return 0;
1407 static int piDEFBW (int isWord) {
1408 for (;;) {
1409 int defined = 0;
1411 if (isStrArg()) {
1412 int f, len = 0;
1413 char *res = getStrArg(&len);
1415 for (f = 0; f < len; ++f) {
1416 int32_t b = (uint8_t)res[f];
1417 b += defIncr;
1418 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
1419 if (b < 0) b += 256;
1420 emitByte(b);
1422 } else {
1423 int32_t res = getExprArg(&defined);
1425 if (pass > 0 && !defined) fatal("undefined operand");
1426 res += defIncr;
1427 if (isWord) {
1428 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
1429 if (res < 0) res += 65536;
1430 if (isWord == 1) emitWord(res); else emitRWord(res);
1431 } else {
1432 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
1433 if (res < 0) res += 256;
1434 emitByte(res);
1437 if (!skipComma()) break;
1439 return 0;
1442 static int piDEFB (void) { return piDEFBW(0); }
1443 static int piDEFW (void) { return piDEFBW(1); }
1444 static int piDEFR (void) { return piDEFBW(2); }
1447 static int piDEFS (void) {
1448 for (;;) {
1449 int32_t bt, f;
1450 int defined = 0;
1451 int32_t res = getExprArg(&defined);
1453 if (pass > 0 && !defined) fatal("undefined operand");
1454 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
1455 if (*curLine && curLine[0] == ',') {
1456 skipComma();
1457 bt = getExprArg(&defined);
1458 if (pass > 0 && !defined) fatal("undefined operand");
1459 bt += defIncr;
1460 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
1461 if (bt < 0) bt += 256;
1462 for (f = 0; f < res; ++f) emitByte(bt);
1463 } else {
1464 pc += res; disp += res;
1466 if (!skipComma()) break;
1468 return 0;
1472 /* bit 0: put '\0' */
1473 /* bit 1: set bit 7 of last byte */
1474 /* bit 2: put length */
1475 static int piDEFSTR (int type) {
1476 for (;;) {
1477 if (isStrArg()) {
1478 int f, len = 0;
1479 char *res = getStrArg(&len);
1481 if (type&0x04) {
1482 if (len > 255) fatal("string too long");
1483 emitByte(len);
1485 for (f = 0; f < len; ++f) {
1486 uint8_t b = (uint8_t)res[f];
1488 if ((type&0x02) && f == len-1) b |= 0x80;
1489 emitByte(b);
1491 if (type&0x01) emitByte(0);
1492 } else {
1493 int defined = 1;
1494 int32_t v = getExprArg(&defined);
1496 if (pass > 0 && !defined) fatal("undefined expression");
1497 if (!defined) v = 0; else v += defIncr;
1498 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
1499 if (v < 0) v += 256;
1500 emitByte(v);
1502 if (!skipComma()) break;
1504 return 0;
1508 static int piDEFM (void) { return piDEFSTR(0x00); }
1509 static int piDEFZ (void) { return piDEFSTR(0x01); }
1510 static int piDEFX (void) { return piDEFSTR(0x02); }
1511 static int piDEFC (void) { return piDEFSTR(0x04); }
1514 ///////////////////////////////////////////////////////////////////////////////
1515 // INCBIN
1517 /* INCBIN "name"[,maxlen] */
1518 static int piINCBIN (void) {
1519 int system = 0;
1520 char *fn, qCh;
1521 uint8_t bt;
1522 FILE *fl;
1523 int maxlen = 65536;
1524 char *args = curLine;
1526 if (!curLine[0]) fatal("INCBIN without file name");
1527 if (isStrArg()) {
1528 qCh = *args++;
1529 system = 0;
1530 } else if (curLine[0] == '<') {
1531 qCh = '<'; ++args;
1532 system = 1;
1533 } else {
1534 qCh = 0;
1535 system = 0;
1537 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
1538 if (!fn[0]) fatal("INCBIN: empty file name");
1539 memmove(curLine, args, strlen(args)+1);
1540 // maxlen
1541 if (curLine[0] == ',') {
1542 int defined = 1;
1544 skipComma();
1545 maxlen = getOneExprArg(&defined);
1546 if (!defined) fatal("INCBIN: undefined maxlen");
1547 if (maxlen < 1) return 1; // nothing to do
1549 // now fix name
1550 if (system) {
1551 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
1552 } else {
1553 sprintf(curLine, "%s", fn);
1556 fl = fopen(curLine, "rb");
1557 if (!fl) fatal("INCBIN: file not found: %s", curLine);
1558 while (maxlen-- > 0) {
1559 int res = fread(&bt, 1, 1, fl);
1561 if (!res) break;
1562 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: %s", curLine); }
1563 emitByte(bt);
1565 fclose(fl);
1566 return 1;
1570 ///////////////////////////////////////////////////////////////////////////////
1571 // MODULE, ENDMODULE
1573 static int piENDMODULE (void) {
1574 if (!curModule) fatal("ENDMODULE without MODULE");
1575 if (curLine[0]) {
1576 char *mn = getOneLabelArg();
1578 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
1580 curModule = NULL;
1581 return 0;
1585 static int piMODULE (void) {
1586 ModuleInfo *mi;
1587 char *mn;
1588 SourceLine *ol = curSrcLine;
1589 int inum;
1591 if (curModule) fatal("no nested modules allowed");
1592 mn = getOneLabelArg();
1593 if (!urIsValidLabelName(mn)) fatal("invalid module name: %s", mn);
1594 mi = moduleFind(mn);
1595 //printf("[%s] %p\n", mn, mi);
1596 if (mi) {
1597 if (mi->seen) {
1598 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
1599 /* skip module */
1600 nextSrcLine(); // skip ourself
1601 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
1602 setCurSrcLine(ol);
1603 fatal("no ENDMODULE");
1605 if (inum == 0) fatal("no nested modules allowed");
1606 curModule = mi;
1607 skipInstruction();
1608 piENDMODULE();
1609 return 0;
1611 } else {
1612 mi = moduleAdd(mn, curSrcLine->fname);
1614 mi->seen = 1;
1615 curModule = mi;
1616 return 0;
1620 ///////////////////////////////////////////////////////////////////////////////
1621 // DUP, EDUP
1623 static int piEDUP (void) {
1624 fatal("EDUP without DUP");
1625 checkExprEnd();
1626 return 1;
1630 static int piDUP (void) {
1631 int defined = 1, dupCnt = 1, inum;
1632 SourceLine *stline, *eline = NULL;
1633 int32_t cnt = getOneExprArg(&defined);
1635 if (!defined) fatal("DUP: counter must be defined");
1636 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
1637 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
1638 // now find corresponding EDUP
1639 // note that we should skip nested DUPs
1640 nextSrcLine(); // skip ourself
1641 stline = curSrcLine;
1642 while (curSrcLine) {
1643 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
1644 // ok, we found something; what is it?
1645 if (inum == 0) {
1646 // new DUP, skip it
1647 ++dupCnt;
1648 nextSrcLine(); // skip DUP
1649 } else {
1650 // EDUP
1651 if (--dupCnt == 0) {
1652 // gotcha!
1653 eline = curSrcLine;
1654 break;
1656 nextSrcLine(); // skip EDUP
1659 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
1660 // now repeat that lines
1661 while (cnt-- > 0) {
1662 setCurSrcLine(stline);
1663 while (curSrcLine != eline) processCurrentLine();
1665 return 1;
1669 ///////////////////////////////////////////////////////////////////////////////
1670 // IF, ENDIF
1672 static int ifCount = 0;
1675 static int ifSkipToEndIfOrElse (int wholeBody) {
1676 int inum, wasElse = 0;
1677 SourceLine *oline;
1679 while (curSrcLine) {
1680 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
1681 switch (inum) {
1682 case 0: /* if */
1683 case 6: /* ifx */
1684 nextSrcLine(); // skip IF
1685 ifSkipToEndIfOrElse(1); // and recurse
1686 nextSrcLine(); // skip ENDIF
1687 break;
1688 case 1: /* else */
1689 if (wasElse) fatal("duplicate ELSE");
1690 if (!wholeBody) return 1;
1691 wasElse = 1;
1692 nextSrcLine(); // skip ELSE
1693 break; // and continue
1694 case 2: /* endif */
1695 return 0; // and exit
1696 case 3: /* elif */
1697 case 7: /* elifx */
1698 if (wasElse) fatal("ELSEIF in ELSE");
1699 if (!wholeBody) return 2;
1700 nextSrcLine(); // skip ELSEIF
1701 break; // and continue
1702 case 4: /* macro */
1703 // skip it as a whole
1704 nextSrcLine(); // skip MACRO
1705 for (;;) {
1706 oline = curSrcLine;
1707 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
1708 if (inum == 1) break;
1709 fatal("invalid nested MACRO");
1711 nextSrcLine(); // skip ENDM
1712 break;
1713 case 5: /* endm */
1714 fatal("unexpected ENDM");
1717 fatal("IF without ENDIF");
1718 return -1;
1722 static int piENDIF (void) {
1723 if (--ifCount < 0) fatal("ENDIF without IF");
1724 checkExprEnd();
1725 return 1;
1729 static int piELSE (void) {
1730 if (--ifCount < 0) fatal("ELSE without IF");
1731 nextSrcLine(); // skip ELSE
1732 ifSkipToEndIfOrElse(1);
1733 return 1;
1737 static int piELSEIF (void) {
1738 if (--ifCount < 0) fatal("ELSEIF without IF");
1739 nextSrcLine(); // skip ELSEIF
1740 ifSkipToEndIfOrElse(1);
1741 return 1;
1745 static int piIFAll (int isIfX) {
1746 int defined = 1;
1747 int ooo = lblOptMakeU2;
1748 lblOptMakeU2 = isIfX ? 1 : 0;
1749 int32_t cond = getOneExprArg(&defined);
1750 lblOptMakeU2 = ooo;
1752 if (!defined) {
1753 if (!isIfX) fatal("IF: condition must be defined");
1754 cond = 0; // for IFX: 0 is there is any undefined label
1756 if (cond) {
1757 // ok, do it until ELSE/ELSEIF/ENDIF
1758 ++ifCount;
1759 return 1;
1761 for (;;) {
1762 int r;
1763 char *args;
1765 nextSrcLine(); // skip last instruction
1766 // skip until ELSE/ELSEIF/ENDIF
1767 r = ifSkipToEndIfOrElse(0);
1768 if (r == 0) break; // ENDIF
1769 if (r == 1) { ++ifCount; break; } // ELSE
1770 // ELSEIF, do condition
1771 if ((args = strIsCommand("ELSEIF", curLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
1772 memmove(curLine, args, strlen(args)+1);
1773 cond = getOneExprArg(&defined);
1774 if (!defined) fatal("ELSEIF: condition must be defined");
1775 if (cond) { ++ifCount; break; } // condition is true
1777 return 1;
1781 static int piIF (void) { return piIFAll(0); }
1782 static int piIFX (void) { return piIFAll(1); }
1785 ///////////////////////////////////////////////////////////////////////////////
1786 // macro processor
1788 * what i did with MACRO is the brain-damaged cheating all the way.
1789 * first, i will collect the MACRO body and remember it.
1790 * second, when the macro is used, i will:
1791 * * create unique labels for all supplied macro args, each with
1792 * number (so IFARG/IFNARG can check the existance)
1793 * * insert the whole macro body in place, fixing argument refs
1794 * * let the asm play with it
1795 * another tricky part is 'local labels': i have to change all
1796 * '..lbl' references -- generate new label name for it and
1797 * replace all refs. and be careful to not touch the strings.
1798 * this is not the best scheme, but it is fairly simple and it wokrs.
1800 static int piMACRO (void) {
1801 fatal("sorry, no MACRO yet");
1802 return 1;
1806 static int piENDM (void) {
1807 fatal("ENDM withoud MACRO");
1808 return 1;
1812 ///////////////////////////////////////////////////////////////////////////////
1813 // line processor
1814 static int optWriteType = 't';
1815 static int optWTChanged = 0;
1818 static void piTapParseLoaderName (void) {
1819 if (skipComma()) {
1820 int len;
1821 char *fn;
1823 if (!isStrArg()) fatal("loader name expected");
1824 fn = getStrArg(&len);
1825 if (len > 10) fatal("loader name too long");
1826 memset(tapeLoaderName, ' ', 10);
1827 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
1832 static int piDEFFMT (void) {
1833 char *name;
1835 if (isStrArg()) {
1836 int len = 0;
1838 name = getStrArg(&len);
1839 } else {
1840 name = getLabelArg();
1842 if (optWTChanged) return 1;
1843 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA")) {
1844 optRunTape = 0;
1845 optRunSNA = (toupper(name[0]) == 'R');
1846 optWriteType = 's';
1847 if (curLine[0]) fatal("too many expressions");
1848 return 1;
1850 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
1851 optRunTape = 0;
1852 optRunSNA = 0;
1853 optWriteType = 't';
1854 piTapParseLoaderName();
1855 return 1;
1857 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE")) {
1858 optRunTape = 1;
1859 optRunSNA = 0;
1860 optWriteType = 't';
1861 piTapParseLoaderName();
1862 return 1;
1864 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
1865 optRunTape = 0;
1866 optRunSNA = 0;
1867 optWriteType = 'r';
1868 if (curLine[0]) fatal("too many expressions");
1869 return 1;
1871 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB")) {
1872 optRunTape = 0;
1873 optRunSNA = 0;
1874 optRunDMB = (toupper(name[0]) == 'R');
1875 optWriteType = 'd';
1876 if (curLine[0]) fatal("too many expressions");
1877 return 1;
1879 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
1880 optRunTape = 0;
1881 optRunSNA = 0;
1882 optWriteType = 'n';
1883 if (curLine[0]) fatal("too many expressions");
1884 return 1;
1886 fatal("invalid default output type: %s", name);
1890 ///////////////////////////////////////////////////////////////////////////////
1891 // line processor
1893 static void processCurrentLine (void) {
1894 if (!curSrcLine) return; // do nothing
1895 loadCurSrcLine();
1896 processLabel();
1897 for (;;) {
1898 char *str, *ee, name[66];
1899 UrAsmOp *op;
1900 int len;
1901 const char *errpos;
1903 removeSpacesAndColons();
1904 // skip spaces and ':'
1905 if (!curLine[0]) { nextSrcLine(); break; }
1906 // try to find and process command
1907 str = curLine; //while (*str && isspace(*str)) ++str; // skip spaces
1908 // find command end
1909 for (ee = str; *ee && !isspace(*ee) && *ee != '"' && *ee != '\''; ++ee) ;
1910 // get command, if any
1911 if (ee != str && ee-str <= 64) {
1912 memset(name, 0, sizeof(name));
1913 memmove(name, str, ee-str);
1914 // known command?
1915 op = urFindOp(name);
1916 if (op) {
1917 // ok, do it
1918 str = ee; while (*str && isspace(*str)) ++str; // skip spaces
1919 memmove(curLine, str, strlen(str)+1);
1920 if (op->fn()) {
1921 nextSrcLine(); // skip it
1922 break;
1927 len = urAssembleOne(curLine, pc, disp, &errpos);
1928 if (len < 0) fatalUrLib(len);
1929 pc += len; disp += len;
1930 if (len >= 0 && errpos) {
1931 memmove(curLine, errpos+1, strlen(errpos));
1932 } else {
1933 nextSrcLine(); // skip it
1934 break;
1940 ///////////////////////////////////////////////////////////////////////////////
1941 // setup instructions
1943 static void registerInstructions (void) {
1944 urAddOp("DISPLAY", piDISPLAY);
1945 urAddOp("DISPLAY0", piDISPLAY0);
1946 urAddOp("DISPLAYA", piDISPLAYA);
1947 urAddOp("DISPHEX", piDISPHEX);
1948 urAddOp("DISPHEX0", piDISPHEX0);
1949 urAddOp("DISPHEXA", piDISPHEXA);
1951 urAddOp("DEFFMT", piDEFFMT);
1953 urAddOp("MACRO", piMACRO);
1954 urAddOp("ENDM", piENDM);
1956 urAddOp("ORG", piORG);
1957 urAddOp("DISP", piDISP);
1958 urAddOp("ENDDISP", piENDDISP);
1959 urAddOp("PHASE", piDISP);
1960 urAddOp("DEPHASE", piENDDISP);
1961 urAddOp("UNPHASE", piENDDISP);
1962 urAddOp("ALIGN", piALIGN);
1963 urAddOp("DISPALIGN", piDISPALIGN);
1964 urAddOp("PHASEALIGN", piDISPALIGN);
1965 urAddOp("ENT", piENT);
1966 urAddOp("CLR", piCLR);
1967 urAddOp("RESERVE", piRESERVE);
1969 urAddOp("INCBIN", piINCBIN);
1971 urAddOp("MODULE", piMODULE);
1972 urAddOp("ENDMODULE", piENDMODULE);
1974 urAddOp("DUP", piDUP);
1975 urAddOp("EDUP", piEDUP);
1977 urAddOp("IF", piIF);
1978 urAddOp("IFX", piIFX);
1979 urAddOp("ELSE", piELSE);
1980 urAddOp("ELSEIF", piELSEIF);
1981 urAddOp("ELSEIFX", piELSEIF);
1982 urAddOp("ENDIF", piENDIF);
1984 urAddOp("DEFINCR", piDEFINCR);
1985 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
1986 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
1987 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
1988 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
1989 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
1990 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
1991 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
1992 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
1996 ///////////////////////////////////////////////////////////////////////////////
1997 // !0: invalid label
1999 static inline void fnSkipSpaces (const char *expr) {
2000 while (*expr && isspace(*expr)) ++expr;
2001 return expr;
2006 static inline char fnNextChar (const char *expr) {
2007 while (*expr && isspace(*expr)) ++expr;
2008 return *expr;
2012 #define FN_CHECK_END do { \
2013 while (*expr && isspace(*expr)) ++expr; \
2014 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
2015 ++expr; \
2016 } while (0)
2019 #define FN_CHECK_COMMA do { \
2020 while (*expr && isspace(*expr)) ++expr; \
2021 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
2022 ++expr; \
2023 } while (0)
2026 static const char *readLabelName (char *buf, const char *expr) {
2027 int pos = 0;
2029 while (*expr && isspace(*expr)) ++expr;
2030 for (;;) {
2031 char ch = *expr++;
2033 if (pos >= 128) return NULL;
2034 if (!ch) break;
2035 if (isalnum(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
2036 buf[pos++] = ch;
2037 } else {
2038 break;
2041 if (pos < 1) return NULL;
2042 buf[pos] = '\0';
2043 if (!urIsValidLabelName(buf)) return NULL;
2044 return expr;
2048 static const char *fnDefKn (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
2049 char lbl[130];
2051 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
2052 FN_CHECK_END;
2053 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
2054 return expr;
2057 static const char *fnDefined (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) { return fnDefKn(res, expr, addr, donteval, defined, error, UR_QTYPE_DEFINED); }
2058 static const char *fnKnown (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) { return fnDefKn(res, expr, addr, donteval, defined, error, UR_QTYPE_KNOWN); }
2061 static const char *fnAligned256 (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2062 expr = urExpressionEx(res, expr, addr, &donteval, defined, error);
2063 FN_CHECK_END;
2064 if (!donteval) res->val = (res->val%256 ? 0 : 1);
2065 return expr;
2069 static const char *fnSameSeg (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2070 URExprValue v0, v1;
2072 urInitExprValue(&v0);
2073 urInitExprValue(&v1);
2074 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2075 FN_CHECK_COMMA;
2076 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2077 FN_CHECK_END;
2078 if (!donteval) res->val = (v0.val/256 == v1.val/256);
2079 return expr;
2083 static const char *fnAlign (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2084 URExprValue v0, v1;
2086 urInitExprValue(&v0);
2087 urInitExprValue(&v1);
2088 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2089 if (fnNextChar(expr) == ',') {
2090 FN_CHECK_COMMA;
2091 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2092 } else {
2093 v1.val = 256;
2095 FN_CHECK_END;
2096 if (!donteval) {
2097 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
2098 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
2100 return expr;
2104 static void registerFunctions (void) {
2105 urExpressionRegisterFunction("defined", fnDefined);
2106 urExpressionRegisterFunction("known", fnKnown);
2107 urExpressionRegisterFunction("aligned256", fnAligned256);
2108 urExpressionRegisterFunction("align", fnAlign);
2109 urExpressionRegisterFunction("sameseg", fnSameSeg);
2113 ///////////////////////////////////////////////////////////////////////////////
2114 // preparing another pass
2116 static void initPass (void) {
2117 curSrcLine = asmText;
2118 curModule = NULL;
2119 pc = disp = ent = 0x100; // viva CP/M!
2120 inTapeBlock = 0;
2121 tapeXorB = 0;
2122 wasOrg = 0;
2123 wasClr = 0;
2124 ifCount = 0;
2125 defIncr = 0;
2126 lblOptMakeU2 = 0;
2127 lastSeenGlobalLabel = strdup(" [MAIN] ");
2128 modulesResetSeen();
2129 prepareMemory();
2133 static int posstPass (void) {
2134 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
2135 if (checkLabels()) return -1;
2136 if (ifCount != 0) fatal("unbalanced IFs");
2137 return 0;
2141 ///////////////////////////////////////////////////////////////////////////////
2142 // options
2144 static struct option longOpts[] = {
2145 {"sna", 0, NULL, 's'},
2146 {"autosna", 0, NULL, 'S'},
2147 {"tap", 0, NULL, 't'},
2148 {"autotap", 0, NULL, 'T'},
2149 /*{"rawtap", 0, NULL, 'T'},*/
2150 {"raw", 0, NULL, 'r'},
2151 {"autodmb", 0, NULL, 'B'},
2152 {"dmb", 0, NULL, 'b'},
2153 {"none", 0, NULL, 'n'},
2154 {"help", 0, NULL, 'h'},
2155 {NULL, 0, NULL, 0}
2159 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
2162 static void usage (const char *pname) {
2163 printf(
2164 "usage: %s [options] infile\n"
2165 "default infiles:", pname);
2166 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
2167 printf("\n"
2168 "options:\n"
2169 " -s --sna write 48K .SNA file\n"
2170 " -S --autosna write 48K .SNA file with autostart\n"
2171 " -t --tap write .tap file\n"
2172 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
2173 " -r --raw write raw file(s)\n"
2174 " -b --dmb write DMB file\n"
2175 " -B --autodmb write DMB file with autostart\n"
2176 " -n --none write nothing\n"
2177 " -h --help this help\n");
2181 ///////////////////////////////////////////////////////////////////////////////
2182 // main
2184 int main (int argc, char *argv[]) {
2185 int res = 0, c;
2186 const char *pname = argv[0];
2187 char *inFile = NULL;
2188 initInclideDir();
2190 urGetByteFn = getByte;
2191 urPutByteFn = putByte;
2192 urFindLabelByNameFn = findLabelCB;
2193 //urIsLabelDefinedOrKnownFn = isLabelDefinedOrKnown;
2195 //strcpy(tapeLoaderName, "cargador ");
2196 tapeLoaderName[0] = 0;
2198 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
2199 while ((c = getopt_long(argc, argv, "sStTbBrnh", longOpts, NULL)) >= 0) {
2200 switch (c) {
2201 case 'S': optRunSNA = 1; optWriteType = 's'; optWTChanged = 1; break;
2202 case 's': optRunSNA = 0; optWriteType = 's'; optWTChanged = 1; break;
2203 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
2204 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
2205 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
2206 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
2207 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
2208 case 'h': usage(pname); res = 0; goto earlyerrquit;
2209 case '?': return 1;
2212 if (optind >= argc) {
2213 // try to find default input file
2214 int f;
2215 for (f = 0; defInFiles[f]; ++f) {
2216 if (!access(defInFiles[f], R_OK)) {
2217 inFile = strdup(defInFiles[f]);
2218 break;
2221 } else {
2222 inFile = strdup(argv[optind]);
2224 if (!inFile || !inFile[0]) {
2225 res = 1;
2226 fprintf(stderr, "ERROR: no input file!\n");
2227 goto earlyerrquit;
2230 registerInstructions();
2231 registerFunctions();
2233 res = asmTextLoad(inFile);
2234 if (!res) {
2236 printf("dumping...\n");
2237 FILE *fo = fopen("z000.out", "w");
2238 if (fo) {
2239 SourceLine *c;
2240 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
2241 fclose(fo);
2244 for (pass = 0; pass <= 1; ++pass) {
2245 initPass();
2246 printf("pass %d\n", pass);
2247 setCurSrcLine(asmText);
2248 if (setjmp(errJP)) { res = 1; break; }
2249 while (curSrcLine) processCurrentLine();
2250 if (posstPass()) { res = 1; break; }
2252 // write result
2253 if (res == 0) {
2254 char *oc = strdup(inFile);
2255 char *pd = strrchr(oc, '.');
2256 if (pd && !strchr(oc, '/')) *pd = '\0';
2257 switch (optWriteType) {
2258 case 's': saveSna(oc); break;
2259 case 't': saveTap(oc); break;
2260 case 'r': saveRaw(oc); break;
2261 case 'd': saveDMB(oc); break;
2263 free(oc);
2265 } else {
2266 fprintf(stderr, "ERROR: loading error!\n");
2269 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
2270 urClearLabels();
2271 modulesClear();
2272 asmTextClear();
2273 urClearOps();
2274 earlyerrquit:
2275 if (inFile) free(inFile);
2276 if (sysIncludeDir) free(sysIncludeDir);
2277 return res?1:0;