urasm: cosmetix
[urasm.git] / src / urasm.c
blobe4ca299313f2d7695e90df5eb2f16f770201b072
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"
21 #include "ursna48.c"
24 #define VERSION_HI 0
25 #define VERSION_MID 1
26 #define VERSION_LO 0
29 #define MAYBE_UNUSED __attribute__((unused))
31 ///////////////////////////////////////////////////////////////////////////////
32 // global variables
34 static char *sysIncludeDir = NULL;
36 #define MAX_LINE_SIZE 16384
37 static char curLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
40 ///////////////////////////////////////////////////////////////////////////////
41 // init dirs
43 static void initInclideDir (void) {
44 const char *id = getenv("URASM_INCLUDE_DIR");
45 if (id && id[0]) {
46 sysIncludeDir = strdup(id);
47 } else {
48 char myDir[4096];
49 memset(myDir, 0, sizeof(myDir));
50 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) strcpy(myDir, ".");
51 else {
52 char *p = (char *)strrchr(myDir, '/');
53 if (!p) strcpy(myDir, "."); else *p = '\0';
55 strcat(myDir, "/libs");
56 sysIncludeDir = strdup(myDir);
58 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
59 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
63 ///////////////////////////////////////////////////////////////////////////////
64 // string utilities
66 /* trim trailing spaces and comments */
67 static void normalizeStr (char *s) {
68 char *p, inQ = 0;
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 (int f = 0; f < 2; ++f, ++p) if (!isxdigit(*p)) break;
79 --p; // last digit will be skiped by 'for'
80 break;
81 case '0': // octal code
82 for (int f = 0; f < 4; ++f, ++p) 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 (int f = 0; f < 3; ++f, ++p) 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 case ':': // reduce colons
105 while (p > s && isspace(p[-1])) --p;
106 *p = ':';
107 //fprintf(stderr, ":[%s]\n", p);
108 if (p[1] && (p[1] == ':' || isspace(p[1]))) {
109 char *e, *t;
111 for (e = p+2; *e && (*e == ':' || isspace(*e)); ++e) ;
112 t = p+1;
113 while ((*t++ = *e++)) ;
114 //fprintf(stderr, "::[%s]\n", p);
116 break;
117 default: ; // do nothing
120 // trim right
121 for (p = s+strlen(s)-1; p >= s && (isspace(*p) || *p == ':'); --p) ;
122 p[1] = '\0';
126 /* returns NULL or pointer to args */
127 /* skips spaces after command if any */
128 static char *strIsCommand (const char *command, char *str) {
129 for (int cnt = 1; cnt > 0; --cnt) {
130 while (*str && isspace(*str)) ++str; // skip spaces
131 for (; *command && *str; ++command, ++str) {
132 if (toupper(*command) != toupper(*str)) return NULL; // alas
134 if (*command) return NULL; // alas
135 if (*str && isalnum(*str)) return NULL; // alas
136 while (*str && isspace(*str)) ++str; // skip spaces
137 if (*str && *str == ':') break; // try again if we have a colon
138 return str; // found
140 return NULL;
144 /* don't free() result */
145 /* skips trailing spaces */
146 static char *parseStr (char **str, char endQ, int *lenp) {
147 static char buf[MAX_LINE_SIZE];
148 int len = 0, n, f, base;
149 char *a = *str;
151 int xDigit (char ch, int base) {
152 if (ch < '0') return -1;
153 if (base <= 10) {
154 if (ch >= '0'+base) return -1;
155 return ch-'0';
157 ch = toupper(ch);
158 if (ch <= '9') return ch-'0';
159 if (ch < 'A' || ch > 'A'+base-10) return -1;
160 ch -= 'A'-10;
161 return (ch < base ? ch : -1);
164 memset(buf, 0, sizeof(buf));
165 if (lenp) *lenp = 0;
166 for (; *a; ++a) {
167 if (*a == '\\') {
168 if (!a[1]) break;
169 switch (*(++a)) {
170 case 'a': buf[len++] = '\a'; break;
171 case 'b': buf[len++] = '\b'; break;
172 case 'e': buf[len++] = '\x1b'; break;
173 case 'f': buf[len++] = '\f'; break;
174 case 'n': buf[len++] = '\n'; break;
175 case 'r': buf[len++] = '\r'; break;
176 case 't': buf[len++] = '\t'; break;
177 case 'v': buf[len++] = '\v'; break;
178 case 'z': buf[len++] = '\0'; break;
179 case 'x': case 'X': // hex
180 ++a; // skip 'x'
181 base = 16; f = 2;
182 donum: for (n = 0; f > 0; --f) {
183 char ch = xDigit(*a++, base);
185 if (ch < 0) { --a; break; }
186 n *= base;
187 n += ch;
189 buf[len++] = n;
190 --a; // return to the last digit, 'for' will skip it
191 break;
192 case '0': // octal
193 base = 8; f = 4;
194 goto donum;
195 case '1' ... '9': // decimal
196 base = 10; f = 3;
197 goto donum;
198 default: buf[len++] = a[0]; break; // others
200 } else {
201 if (*a == endQ) { ++a; break; }
202 buf[len++] = *a;
205 while (*a && isspace(*a)) ++a; // skip trailing spaces
206 *str = a;
207 buf[len] = '\0';
208 if (lenp) *lenp = len;
209 return buf;
213 ///////////////////////////////////////////////////////////////////////////////
214 // source file stack, reader, etc
216 typedef struct SourceLine SourceLine;
217 struct SourceLine {
218 SourceLine *next;
219 char *line;
220 char *fname;
221 int lineNo;
224 static SourceLine *asmText = NULL;
225 static SourceLine *asmTextLast = NULL;
226 static SourceLine *curSrcLine = NULL;
229 static void asmTextClear (void) {
230 while (asmText) {
231 SourceLine *l = asmText;
233 asmText = asmText->next;
234 free(l->line);
235 free(l->fname);
236 free(l);
238 asmTextLast = curSrcLine = NULL;
242 static void normIncName (char *dest, const char *fn, int system) {
243 struct stat st;
245 if (system) sprintf(dest, "%s/%s", sysIncludeDir, fn); else sprintf(dest, "%s", fn);
246 if (stat(dest, &st)) return;
247 if (S_ISDIR(st.st_mode)) strcat(dest, "/zzmain.zas");
251 static int asmTextLoad (const char *fname) {
252 FILE *fl;
253 int lineNo = 0;
254 char *args;
255 SourceLine *s;
256 static int includeCount = 0;
258 if (!(fl = fopen(fname, "r"))) {
259 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
260 return -1;
262 printf("loading: %s\n", fname);
263 // read file
264 while (fgets(curLine, sizeof(curLine)-1, fl)) {
265 ++lineNo;
266 curLine[sizeof(curLine)-1] = '\0';
267 normalizeStr(curLine);
268 //fprintf(stderr, "*[%s]\n", curLine);
269 if (!curLine[0]) continue; // don't store empty lines
270 // find specials, if any
271 if (isspace(curLine[0])) {
272 if ((args = strIsCommand("INCLUDE", curLine)) != NULL) {
273 // process 'INCLUDE'
274 int system = 0;
275 char qCh = 0, *fn;
277 if (!args[0]) {
278 fclose(fl);
279 fprintf(stderr, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname, lineNo);
280 return -1;
282 if (args[0] == '"' || args[0] == '\'') qCh = *args++;
283 else if (args[0] == '<') { qCh = '>'; ++args; system = 1; }
285 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
286 if (!fn[0]) {
287 fclose(fl);
288 fprintf(stderr, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname, lineNo);
289 return -1;
291 if (args[0]) {
292 fclose(fl);
293 fprintf(stderr, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname, lineNo);
294 return -1;
296 if (includeCount > 256) {
297 fclose(fl);
298 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname, lineNo);
299 return -1;
301 normIncName(curLine, fn, system);
302 //if (system) sprintf(curLine, "%s/%s", sysIncludeDir, fn); else sprintf(curLine, "%s", fn);
303 if ((fn = strdup(curLine)) == NULL) abort();
304 ++includeCount;
305 system = asmTextLoad(fn);
306 --includeCount;
307 free(fn);
308 if (system) { fclose(fl); return system; }
309 continue;
312 // add current line
313 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
314 s->lineNo = lineNo;
315 if ((s->line = strdup(curLine)) == NULL) abort();
316 if ((s->fname = strdup(fname)) == NULL) abort();
317 if (asmTextLast) asmTextLast->next = s; else asmText = s;
318 asmTextLast = s;
320 fclose(fl);
321 return 0;
325 static inline void loadCurSrcLine (void) { if (curSrcLine) strcpy(curLine, (curSrcLine != NULL ? curSrcLine->line : "")); }
326 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
327 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
330 ///////////////////////////////////////////////////////////////////////////////
331 // prototypes
333 static void processCurrentLine (void); // only one, will skip to next one
336 ///////////////////////////////////////////////////////////////////////////////
337 // error raisers, etc
339 static jmp_buf errJP;
342 static void errorWriteFile (void) {
343 if (curSrcLine) {
344 fprintf(stderr, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
345 } else {
346 fprintf(stderr, "somewhere in time: ");
350 static void errorMsgV (const char *fmt, va_list ap) {
351 errorWriteFile();
352 vfprintf(stderr, fmt, ap);
353 va_end(ap);
354 fputc('\n', stderr);
355 fflush(stderr);
359 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
360 va_list ap;
361 fprintf(stderr, "WARNING ");
362 va_start(ap, fmt);
363 errorMsgV(fmt, ap);
367 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
368 va_list ap;
369 fprintf(stderr, "FATAL ");
370 va_start(ap, fmt);
371 errorMsgV(fmt, ap);
375 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
376 va_list ap;
377 va_start(ap, fmt);
378 errorMsgV(fmt, ap);
379 longjmp(errJP, 666);
383 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
384 errorMsg("%s", urErrorMessage(errcode));
385 longjmp(errJP, 666);
389 //////////////////////////////////////////////////////////////////////////////
390 // operator management
392 typedef int (*UrAsmOpFn) (void);
394 typedef struct UrAsmOp {
395 char *name;
396 UrAsmOpFn fn;
397 struct UrAsmOp *next;
398 } UrAsmOp;
400 static UrAsmOp *oplist = NULL;
403 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
404 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
405 if (!res) abort();
406 res->name = strdup(name);
407 res->fn = fn;
408 res->next = oplist;
409 oplist = res;
410 return res;
414 static UrAsmOp *urFindOp (const char *name) {
415 UrAsmOp *res;
416 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
417 return res;
421 static void urClearOps (void) {
422 while (oplist) {
423 UrAsmOp *c = oplist;
425 oplist = oplist->next;
426 free(c->name);
427 free(c);
432 ///////////////////////////////////////////////////////////////////////////////
433 // label management
435 typedef struct UrLabelInfo {
436 char *name;
437 int32_t value;
438 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
439 int known; /* !0: label value already known */
440 int refLine; /* first referenced line */
441 char *refFile;
442 struct UrLabelInfo *next;
443 } UrLabelInfo;
445 static UrLabelInfo *labels = NULL;
448 static void urClearLabels (void) {
449 UrLabelInfo *c;
450 while ((c = labels)) {
451 labels = labels->next;
452 if (c->name) free(c->name);
453 if (c->refFile) free(c->refFile);
454 free(c);
459 static UrLabelInfo *urFindLabel (const char *name) {
460 UrLabelInfo *c;
461 for (c = labels; c; c = c->next) if (!strcmp(name, c->name)) break;
462 return c;
466 static UrLabelInfo *urAddLabel (const char *name) {
467 UrLabelInfo *c = urFindLabel(name);
468 if (!c) {
469 UrLabelInfo *p;
470 for (p = NULL, c = labels; c; p = c, c = c->next) ;
471 c = calloc(1, sizeof(UrLabelInfo));
472 if (!c) abort();
473 c->name = strdup(name);
474 c->type = -1;
475 if (p) p->next = c; else labels = c;
476 c->next = NULL;
478 return c;
482 ///////////////////////////////////////////////////////////////////////////////
483 // module list management
485 typedef struct ModuleInfo {
486 char *name;
487 char *fname; // opened in this file
488 int seen; // !0: module already seen, skip other definitions from the same file
489 struct ModuleInfo *next;
490 } ModuleInfo;
492 static ModuleInfo *modules = NULL;
493 static ModuleInfo *curModule = NULL;
496 static void modulesClear (void) {
497 curModule = NULL;
498 while (modules) {
499 ModuleInfo *c = modules;
501 modules = modules->next;
502 free(c->name);
503 free(c->fname);
504 free(c);
509 static void modulesResetSeen (void) {
510 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
514 static ModuleInfo *moduleFind (const char *name) {
515 if (!name || !name[0]) return NULL;
516 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
517 return NULL;
521 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
522 ModuleInfo *c;
524 if (!name || !fname || !name[0] || !fname[0]) abort();
525 if ((c = calloc(1, sizeof(ModuleInfo))) == NULL) abort();
526 if ((c->name = strdup(name)) == NULL) abort();
527 if ((c->fname = strdup(fname)) == NULL) abort();
528 c->next = modules;
529 return (modules = c);
533 ///////////////////////////////////////////////////////////////////////////////
534 // destination memory management
536 static uint8_t memory[65536];
537 static char memused[65536];
538 static char memresv[65536];
539 static uint16_t pc = 0; /* current position to write */
540 static uint16_t disp = 0; /* current 'virtual PC' */
541 static uint16_t ent = 0; /* starting address */
542 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
543 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
544 static int inTapeBlock = 0;
545 static uint8_t tapeXorB = 0;
548 static inline uint8_t getByte (uint16_t addr) {
549 return memory[addr];
554 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
555 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
560 static inline void putByte (uint16_t addr, uint8_t b) {
561 if (inTapeBlock) tapeXorB ^= b;
562 memory[addr] = b;
563 memused[addr] = 1;
567 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
568 putByte(addr, w&0xFFU);
569 putByte(addr+1, (w>>8)&0xFFU);
573 static inline void emitByte (uint8_t b) {
574 putByte(pc, b);
575 ++pc;
576 ++disp;
580 static inline void emitWord (uint16_t w) {
581 emitByte(w&0xFFU);
582 emitByte((w>>8)&0xFFU);
586 static inline void emitRWord (uint16_t w) {
587 emitByte((w>>8)&0xFFU);
588 emitByte(w&0xFFU);
592 static void prepareMemory (void) {
593 memset(memory, 0, sizeof(memory));
594 memset(memused, 0, sizeof(memused));
595 memset(memresv, 0, sizeof(memresv));
599 ///////////////////////////////////////////////////////////////////////////////
600 // label getter and utilities
602 static char *lastSeenGlobalLabel = NULL; /* global */
605 static char *fixLocalLabel (const char *name) {
606 static char newname[MAX_LINE_SIZE*2+1024];
608 memset(newname, 0, sizeof(newname));
609 if (!name || !name[0]) {
610 newname[0] = '\0';
611 } else if (!lastSeenGlobalLabel || name[0] != '.') {
612 strcpy(newname, name);
613 } else {
614 // this is local label, let's rename it
615 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
617 return newname;
621 static char *fixGlobalLabel (const char *name) {
622 static char newname[MAX_LINE_SIZE*2+1024];
624 memset(newname, 0, sizeof(newname));
625 if (!name || !name[0]) {
626 newname[0] = '\0';
627 } else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
628 if (name[0] == '@' && name[1]) ++name;
629 strcpy(newname, name);
630 } else {
631 // this is global unqualified label and we have a module; let's rename it
632 sprintf(newname, "%s.%s", curModule->name, name);
634 //printf("%s --> %s\n", name, newname);
635 return newname;
639 static int lblOptMakeU2 = 0;
641 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found) {
642 UrLabelInfo *lbl;
643 char *ln, *nn;
645 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
646 lbl = urFindLabel(nn);
647 if (!lbl) {
648 // try non-module label
649 lbl = urFindLabel(ln);
651 if (!lbl) {
652 if (pass != 0) {
653 errorMsg("using undefined label %s", ln);
654 *found = 0;
655 *defined = 0;
656 return 0;
658 lbl = urAddLabel(nn);
659 lbl->type = lblOptMakeU2 ? -42 : -1;
660 lbl->known = 0;
661 lbl->refLine = curSrcLine->lineNo;
662 lbl->refFile = strdup(curSrcLine->fname);
663 //printf("new label: [%s]\n", lbl->name);
664 } else {
665 //printf("label reference: [%s]\n", lbl->name);
667 if (lbl) {
668 *found = 1;
669 *defined = lbl->known!=0;
670 return lbl->value;
672 *found = 0;
673 *defined = 0;
674 return 0;
678 // qtypes
679 enum {
680 UR_QTYPE_DEFINED,
681 UR_QTYPE_KNOWN
684 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
685 UrLabelInfo *lbl;
686 char *ln, *nn;
688 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
689 lbl = urFindLabel(nn);
690 if (!lbl) {
691 // try non-module label
692 lbl = urFindLabel(ln);
694 switch (qtype) {
695 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
696 case UR_QTYPE_KNOWN: return lbl!=NULL;
697 default: ;
699 return 0;
703 static int checkLabels (void) {
704 int wasError = 0;
706 for (UrLabelInfo *c = labels; c; c = c->next) {
707 if (c->type == -1) {
708 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
709 wasError = 1;
711 if (c->type == 0) c->known = -1;
713 //if (wasError) longjmp(errJP, 667);
714 return wasError;
718 ///////////////////////////////////////////////////////////////////////////////
719 // expression utils
721 static inline char *strSkipSpaces (char *s) {
722 while (*s && isspace(*s)) ++s;
723 return s;
727 static inline char *strSkipSpacesColons (char *s) {
728 while (*s && (isspace(*s) || *s == ':')) ++s;
729 return s;
733 static int32_t getExprArg (int *defined) {
734 int error = 0;
735 char *a = strSkipSpaces(curLine);
736 const char *ee;
737 int32_t res;
739 if (!a[0]) fatal("expression expected");
740 ee = urExpression(&res, a, disp, defined, &error);
741 if (error) fatalUrLib(error);
742 if (*ee) {
743 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
744 memmove(curLine, ee, strlen(ee)+1);
745 } else {
746 curLine[0] = '\0';
748 return res;
752 static int32_t getOneExprArg (int *defined) {
753 int32_t res = getExprArg(defined);
755 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
756 return res;
760 static int isStrArg (void) { return (curLine[0] == '"' || curLine[0] == '\''); }
763 static char *getStrArg (int *lenp) {
764 char *res, qCh;
765 char *a = strSkipSpaces(curLine);
767 qCh = *a++;
768 if (qCh != '"' && qCh != '\'') fatal("string expected");
769 res = parseStr(&a, qCh, lenp);
770 if (*a) {
771 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
772 memmove(curLine, a, strlen(a)+1);
773 } else {
774 curLine[0] = '\0';
776 return res;
780 static MAYBE_UNUSED char *getOneStrArg (int *lenp) {
781 char *res = getStrArg(lenp);
783 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
784 return res;
788 static char *getLabelArg (void) {
789 static char res[MAX_LINE_SIZE+128], *p;
790 char *a = strSkipSpaces(curLine);
792 memset(res, 0, sizeof(res));
793 if (!a[0]) fatal("label expected");
794 for (p = res; *a && *a != ','; ++a, ++p) *p = *a;
795 for (; p > res && isspace(p[-1]); --p) ;
796 *p = '\0';
797 if (p-res > 120) fatal("label name too long: %s", res);
798 while (*a && isspace(*a)) ++a;
799 if (*a) {
800 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
801 memmove(curLine, a, strlen(a)+1);
802 } else {
803 curLine[0] = '\0';
805 return res;
809 static char *getOneLabelArg (void) {
810 char *res = getLabelArg();
812 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
813 return res;
817 /* res == 0: end of expression */
818 static int skipComma (void) {
819 char *a = strSkipSpaces(curLine);
821 if (!a[0]) { curLine[0] = '\0'; return 0; }
822 if (a[0] == ':') {
823 while (a[0] && (a[0] == ':' || isspace(a[0]))) ++a;
824 if (!a[0]) { curLine[0] = '\0'; return 0; }
825 memmove(curLine, a, strlen(a)+1);
826 return -1;
828 if (a[0] != ',') fatal("invalid expression: ',' expected");
829 for (++a; *a && isspace(*a); ++a) ;
830 if (!a[0]) { curLine[0] = '\0'; return 0; }
831 memmove(curLine, a, strlen(a)+1);
832 return 1;
836 // ???
837 static void skipInstruction (void) {
838 char *str = curLine;
840 for (int cnt = 1; cnt > 0; --cnt) {
841 while (*str && isspace(*str)) ++str; // skip spaces
842 while (*str && !isspace(*str)) ++str; // skip non-spaces
843 while (*str && isspace(*str)) ++str; // skip spaces
844 if (!str[0] || *str != ':') break;
845 // try again if we have a colon
847 memmove(curLine, str, strlen(str)+1);
851 ///////////////////////////////////////////////////////////////////////////////
852 // label processor
854 static MAYBE_UNUSED void removeSpacesAndColons (void) {
855 char *ep = strSkipSpacesColons(curLine);
857 memmove(curLine, ep, strlen(ep)+1);
861 static void checkExprEnd (void) {
862 char *ep = strSkipSpaces(curLine);
864 memmove(curLine, ep, strlen(ep)+1);
865 if (curLine[0] && curLine[0] != ':') fatal("end of expression expected");
869 /* remove label from curLine */
870 static void removeLabel (void) {
871 char *ep = curLine;
873 if (ep[0] && !isspace(ep[0])) for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ; // skip text
874 // skip spaces and colons
875 ep = strSkipSpacesColons(ep);
876 memmove(curLine, ep, strlen(ep)+1);
880 static void processLabel (void) {
881 char *argstart;
882 char *ep, *ln, *nn;
883 static char n2[256];
884 UrLabelInfo *lbl;
885 int noLocAff = 0, doEQU = 0;
887 memset(n2, 0, sizeof(n2));
888 if (!curLine[0] || isspace(curLine[0]) || curLine[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
889 // collect label
890 for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ;
891 if (ep-curLine > 120) fatal("label too long");
892 // copy label
893 memset(n2, 0, sizeof(n2));
894 memmove(n2, curLine, ep-curLine);
895 if (!urIsValidLabelName(n2)) return; // this is not a valid label, get out of here
896 // check if this can be instruction
897 while (*ep && isspace(*ep)) ++ep;
898 if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
899 // ok, we got a good label
900 removeLabel();
901 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
902 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
903 lbl = urAddLabel(nn);
904 if (!lbl->refFile) {
905 lbl->refLine = curSrcLine->lineNo;
906 lbl->refFile = strdup(curSrcLine->fname);
908 //printf("new: [%s]\n", lbl->name);
909 // get command name
910 if (curLine[0] == '=') {
911 doEQU = 0;
912 argstart = curLine+1;
913 } else {
914 doEQU = 1;
915 argstart = strIsCommand("EQU", curLine);
917 if (!argstart || doEQU) {
918 if (pass == 0 && lbl->type != -1) fatal("duplicate label %s", lbl->name);
920 if (argstart) {
921 // do '=' or 'EQU'
922 memmove(curLine, argstart, strlen(argstart)+1);
923 if (!doEQU) {
924 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label %s", lbl->name);
926 int defined = 1;
927 int32_t res = getOneExprArg(&defined);
928 lbl->type = doEQU;
929 if (defined) {
930 lbl->value = res;
931 lbl->known = 1;
932 } else {
933 if (pass != 0) fatal("can't calculate label %s", lbl->name);
935 curLine[0] = '\0';
936 return;
938 // code label
939 if (lbl->name[0] != '{' && !noLocAff) {
940 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
941 lastSeenGlobalLabel = strdup(lbl->name);
943 lbl->type = 2;
944 lbl->value = disp;
945 lbl->known = 1;
949 ///////////////////////////////////////////////////////////////////////////////
950 // instruction finder (in source)
952 /* array ends with NULL */
953 /* returns line or NULL */
954 /* iidx will be set to found instruction number */
955 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
956 if (iidx) *iidx = -1;
957 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
958 va_list ap;
960 //if (!isspace(cur->line[0])) continue; // fuck labeled strings
961 va_start(ap, iidx);
962 for (int f = 0; ;++f) {
963 const char *name = va_arg(ap, const char *);
965 if (!name) break;
966 if (strIsCommand(name, cur->line)) {
967 va_end(ap);
968 if (iidx) *iidx = f;
969 return cur;
972 va_end(ap);
974 return NULL;
978 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
979 return findNextInstructionFromList(NULL, name, NULL);
983 ///////////////////////////////////////////////////////////////////////////////
984 // writers
986 static int optRunTape = 1;
987 static int optRunDMB = 1;
988 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
989 static int optSNA48 = 1;
992 /* return 'found' flag */
993 static int findChunkFrom (int addr, int *start, int *len) {
994 if (addr < 0) addr = 0;
995 for (; addr <= 65535 && !memused[addr]; ++addr) ;
996 if (addr > 65535) return 0;
997 *start = addr;
998 for (; addr <= 65535 && memused[addr]; ++addr) ;
999 *len = addr-(*start);
1000 return 1;
1004 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
1005 for (; buflen >= 2; buflen -= 2) {
1006 int cnt = *buf++;
1007 uint8_t byte = *buf++;
1009 //printf("%d %u %u\n", cnt+1, byte, addr);
1010 if (cnt == 0) {
1011 // copy
1012 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
1013 if (!memused[addr]) putByte(addr, *buf);
1014 if (addr == 0xffff) return;
1016 } else {
1017 // fill
1018 for (; cnt > 0; --cnt, ++addr) {
1019 if (!memused[addr]) putByte(addr, byte);
1020 if (addr == 0xffff) return;
1027 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1028 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1029 if (bxor) *bxor = (*bxor)^b;
1030 return 0;
1034 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1035 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1036 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1037 return 0;
1041 ///////////////////////////////////////////////////////////////////////////////
1042 // .sna
1044 static int saveSna (const char *fname, int as48) {
1045 char *fn = malloc(strlen(fname)+16);
1046 uint8_t regs[27];
1047 FILE *fo;
1048 char abuf[32];
1050 sprintf(fn, "%s.sna", fname);
1051 fo = fopen(fn, "wb");
1052 free(fn);
1053 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1054 printf("out: %s.sna\n", fname);
1055 memmove(regs, ursna48, 27);
1056 #if 0
1057 if (optRunSNA) {
1058 /* push new address */
1059 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1060 sp -= 2;
1061 putByte(sp, ent&0xFFU);
1062 putByte(sp+1, (ent>>8)&0xFFU);
1063 regs[23] = sp&0xFFU;
1064 regs[24] = (sp>>8)&0xFFU;
1066 fwrite(regs, 27, 1, fo);
1067 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1068 fwrite(memory+16384, 49152, 1, fo);
1069 #endif
1071 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1073 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1074 //fprintf(stderr, "%d\n", memused[sp]);
1075 if (memused[sp] || memused[(sp+1)&0xffff]) {
1076 fprintf(stderr, "FATAL: can't save snapshot!\n");
1077 goto error;
1080 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
1081 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
1082 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1084 sprintf(abuf, "%05d", clrAddr);
1085 memcpy(memory+23762, abuf, 5);
1086 sprintf(abuf, "%05d", ent);
1087 memcpy(memory+23773, abuf, 5);
1089 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
1091 if (!as48) {
1092 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
1094 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
1095 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
1096 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
1097 // write pages
1098 memset(memory, 0, 16384);
1099 for (int f = 1; f < 8; ++f) {
1100 if (f != 2 && f != 5) {
1101 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
1106 fclose(fo);
1107 return 0;
1108 error:
1109 fclose(fo);
1110 unlink(fname);
1111 return -1;
1115 ///////////////////////////////////////////////////////////////////////////////
1116 // bin chunks
1118 static void saveRaw (const char *basename) {
1119 char *fname = malloc(strlen(basename)+16);
1120 int start = 0, len;
1121 FILE *fo;
1123 while (findChunkFrom(start, &start, &len)) {
1124 sprintf(fname, "%s_%04X.%s", basename, start, optTapExt?"tap":"bin");
1125 fo = fopen(fname, "wb");
1126 if (!fo) {
1127 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1128 } else {
1129 printf("out: %s\n", fname);
1130 if (fwrite(memory+start, len, 1, fo) != 1) {
1131 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1132 fclose(fo);
1133 unlink(fname);
1134 } else {
1135 fclose(fo);
1138 start += len;
1140 free(fname);
1144 ///////////////////////////////////////////////////////////////////////////////
1145 // .dmb
1147 static int fwriteW16 (FILE *fo, uint16_t w) {
1148 uint8_t b = w&0xff;
1150 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1151 b = (w>>8)&0xff;
1152 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1153 return 0;
1157 static int saveDMB (const char *fname) {
1158 char *fn = malloc(strlen(fname)+16);
1159 int start = 0, len;
1160 uint16_t pcnt = 0;
1161 FILE *fo;
1163 sprintf(fn, "%s.dmb", fname);
1164 fo = fopen(fn, "wb");
1165 free(fn);
1166 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
1168 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
1170 printf("out: %s.dmb\n", fname);
1171 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
1172 if (fwriteW16(fo, (optRunDMB ? ent : 0)) != 0) goto error;
1173 if (fwriteW16(fo, clrAddr) != 0) goto error;
1174 if (fwriteW16(fo, pcnt) != 0) goto error;
1176 start = 0;
1177 while (findChunkFrom(start, &start, &len)) {
1178 if (fwriteW16(fo, len) != 0) goto error;
1179 if (fwriteW16(fo, start) != 0) goto error;
1180 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
1181 start += len;
1183 fclose(fo);
1184 return 0;
1185 error:
1186 fclose(fo);
1187 unlink(fname);
1188 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
1189 return -1;
1193 ///////////////////////////////////////////////////////////////////////////////
1194 // .tap
1196 static char tapeLoaderName[16];
1199 static void saveTapCargador (FILE *fo) {
1200 // count blocks
1201 static uint8_t cargador[16384]; // should be enough for everyone
1202 int start = 0, len, pos, f;
1203 uint8_t bxor;
1206 void putStr (const char *s) {
1207 for (; *s; ++s) cargador[pos++] = *s;
1210 void putNum (int num) {
1211 char buf[64];
1212 sprintf(buf, "%d", num);
1213 putStr(buf);
1217 pos = 4;
1218 // number
1219 cargador[0] = 0; cargador[1] = 10;
1220 // size (will be fixed later)
1221 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
1222 // load blocks
1223 while (findChunkFrom(start, &start, &len)) {
1224 // :LOAD "" CODE
1225 putStr(":\xef\"\"\xaf");
1226 start += len;
1228 // and run
1229 // :RANDOMIZE USR VAL "xxx"
1230 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
1231 // patch len
1232 cargador[2] = (pos-4)&0xff;
1233 cargador[3] = ((pos-4)>>8)&0xff;
1234 // write header
1235 fWriteWord(fo, 19, NULL); // length of header
1236 bxor = 0;
1237 fWriteByte(fo, 0, &bxor); // header block
1238 fWriteByte(fo, 0, &bxor); // 'basic' flag
1239 if (tapeLoaderName[0]) {
1240 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1241 } else {
1242 fWriteByte(fo, 'c', &bxor);
1243 fWriteByte(fo, 'a', &bxor);
1244 fWriteByte(fo, 'r', &bxor);
1245 fWriteByte(fo, 'g', &bxor);
1246 fWriteByte(fo, 'a', &bxor);
1247 fWriteByte(fo, 'd', &bxor);
1248 fWriteByte(fo, 'o', &bxor);
1249 fWriteByte(fo, 'r', &bxor);
1250 fWriteByte(fo, ' ', &bxor);
1251 fWriteByte(fo, ' ', &bxor);
1253 fWriteWord(fo, pos, &bxor); // length
1254 fWriteWord(fo, 10, &bxor); // start
1255 fWriteWord(fo, pos, &bxor); // length2
1256 fWriteByte(fo, bxor, NULL);
1257 // write data
1258 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
1259 bxor = 0;
1260 fWriteByte(fo, 0xFFU, &bxor); // data block
1261 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
1262 fWriteByte(fo, bxor, NULL);
1266 static void saveTap (const char *basename) {
1267 char *fname = malloc(strlen(basename)+16);
1268 char blkname[128];
1269 int start = 0, len, f;
1270 uint8_t bxor;
1271 FILE *fo;
1273 sprintf(fname, "%s.tap", basename);
1274 fo = fopen(fname, "wb");
1275 free(fname);
1276 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
1277 printf("out: %s.tap\n", basename);
1278 if (optRunTape) saveTapCargador(fo);
1279 while (findChunkFrom(start, &start, &len)) {
1280 // write header
1281 if (tapeLoaderName[0]) {
1282 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1283 memcpy(blkname, tapeLoaderName, 10);
1284 } else {
1285 sprintf(blkname, "c%04X:%04X", start, len);
1287 //printf(" block: %s\n", blkname);
1288 fWriteWord(fo, 19, NULL); // length of header
1289 bxor = 0;
1290 fWriteByte(fo, 0, &bxor); // header block
1291 fWriteByte(fo, 3, &bxor); // 'code' flag
1292 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
1293 fWriteWord(fo, len, &bxor);
1294 fWriteWord(fo, start, &bxor);
1295 fWriteWord(fo, 32768, &bxor);
1296 fWriteByte(fo, bxor, NULL);
1297 // write data
1298 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
1299 bxor = 0;
1300 fWriteByte(fo, 0xFFU, &bxor); // data block
1301 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
1302 fWriteByte(fo, bxor, NULL);
1303 start += len;
1305 fclose(fo);
1309 ///////////////////////////////////////////////////////////////////////////////
1310 // pseudoinstructions
1312 // note that processCurrentLine() will NOT skip to the next line before
1313 // calling pseudoinstruction handler!
1314 // note that processCurrentLine() will skip current line after calling
1315 // pseudoinstruction handler!
1317 static int wasOrg = 0;
1318 static int wasClr = 0;
1321 ///////////////////////////////////////////////////////////////////////////////
1322 // user warnings
1324 static int piDISPLAYX (int passNo, int asHex) {
1325 for (;;) {
1326 if (isStrArg()) {
1327 int len = 0;
1328 char *res = getStrArg(&len);
1330 if (passNo < 0 || pass == passNo) printf("%s", res);
1331 } else {
1332 int defined = 1;
1333 int32_t v = getExprArg(&defined);
1335 if (passNo < 0 || pass == passNo) {
1336 if (asHex) printf("%04X", (unsigned int)v);
1337 else printf("%d", v);
1340 if (!skipComma()) break;
1342 return 0;
1346 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
1347 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
1348 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
1349 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
1350 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
1351 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
1354 ///////////////////////////////////////////////////////////////////////////////
1355 // ORG, DISP, etc.
1357 static int piORG (void) {
1358 int defined = 1;
1359 int32_t res = getOneExprArg(&defined);
1361 if (!defined) fatal("sorry, ORG operand value must be known here");
1362 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
1363 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
1364 pc = disp = res;
1365 if (!wasOrg) {
1366 wasOrg = 1;
1367 ent = res;
1368 if (!wasClr && res > 0) clrAddr = res-1;
1370 return 0;
1374 static int piDISP (void) {
1375 int defined = 1;
1376 int32_t res = getOneExprArg(&defined);
1378 if (!defined) fatal("sorry, DISP operand value must be known here");
1379 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
1380 //printf("DISP=%d\n", res);
1381 disp = res;
1382 return 0;
1386 static int piENDDISP (void) {
1387 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1388 checkExprEnd();
1389 disp = pc;
1390 return 0;
1394 static int piENT (void) {
1395 int defined = 1;
1396 int32_t res = getOneExprArg(&defined);
1398 //if (!defined) fatal("sorry, ENT operand value must be known here");
1399 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
1400 ent = res;
1401 return 0;
1405 static int piCLR (void) {
1406 int defined = 1;
1407 int32_t res = getOneExprArg(&defined);
1409 //if (!defined) fatal("sorry, CLR operand value must be known here");
1410 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
1411 clrAddr = res;
1412 wasClr = 1;
1413 return 0;
1417 static int piRESERVE (void) {
1419 int defined = 1, start;
1420 int32_t res = getExprArg(&defined);
1421 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1422 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1423 start = res;
1424 if (!skipComma()) fatal("RESERVE needs 2 args");
1425 res = getOneExprArg(&defined);
1426 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1427 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1428 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
1430 fatal("RESERVE: not yet!");
1431 return 0;
1435 static int piALIGN (void) {
1436 int defined = 1;
1437 int32_t res;
1439 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1440 res = getOneExprArg(&defined);
1441 if (!defined) fatal("sorry, ALIGN operand value must be known here");
1442 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
1443 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
1444 if (res > 0 && pc%res != 0) {
1445 pc /= res;
1446 pc *= res;
1447 pc += res;
1448 disp = pc;
1450 return 0;
1454 static int piDISPALIGN (void) {
1455 int defined = 1;
1456 int32_t res = getOneExprArg(&defined);
1458 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
1459 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
1460 if (res > 0 && disp%res != 0) {
1461 disp /= res;
1462 disp *= res;
1463 disp += res;
1465 return 0;
1469 ///////////////////////////////////////////////////////////////////////////////
1470 // DEFx
1472 static int defIncr = 0;
1475 static int piDEFINCR (void) {
1476 int defined = 1;
1477 int32_t cnt = getOneExprArg(&defined);
1479 if (!defined) fatal("DEFINCR: increment must be defined");
1480 defIncr = cnt;
1481 return 0;
1485 static int piDEFBW (int isWord) {
1486 for (;;) {
1487 int defined = 0;
1489 if (isStrArg()) {
1490 int f, len = 0;
1491 char *res = getStrArg(&len);
1493 for (f = 0; f < len; ++f) {
1494 int32_t b = (uint8_t)res[f];
1495 b += defIncr;
1496 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
1497 if (b < 0) b += 256;
1498 emitByte(b);
1500 } else {
1501 int32_t res = getExprArg(&defined);
1503 if (pass > 0 && !defined) fatal("undefined operand");
1504 res += defIncr;
1505 if (isWord) {
1506 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
1507 if (res < 0) res += 65536;
1508 if (isWord == 1) emitWord(res); else emitRWord(res);
1509 } else {
1510 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
1511 if (res < 0) res += 256;
1512 emitByte(res);
1515 if (!skipComma()) break;
1517 return 0;
1520 static int piDEFB (void) { return piDEFBW(0); }
1521 static int piDEFW (void) { return piDEFBW(1); }
1522 static int piDEFR (void) { return piDEFBW(2); }
1525 static int piDEFS (void) {
1526 for (;;) {
1527 int32_t bt, f;
1528 int defined = 0;
1529 int32_t res = getExprArg(&defined);
1531 if (pass > 0 && !defined) fatal("undefined operand");
1532 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
1533 if (*curLine && curLine[0] == ',') {
1534 skipComma();
1535 bt = getExprArg(&defined);
1536 if (pass > 0 && !defined) fatal("undefined operand");
1537 bt += defIncr;
1538 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
1539 if (bt < 0) bt += 256;
1540 for (f = 0; f < res; ++f) emitByte(bt);
1541 } else {
1542 pc += res; disp += res;
1544 if (!skipComma()) break;
1546 return 0;
1550 /* bit 0: put '\0' */
1551 /* bit 1: set bit 7 of last byte */
1552 /* bit 2: put length */
1553 static int piDEFSTR (int type) {
1554 for (;;) {
1555 if (isStrArg()) {
1556 int f, len = 0;
1557 char *res = getStrArg(&len);
1559 if (type&0x04) {
1560 if (len > 255) fatal("string too long");
1561 emitByte(len);
1563 for (f = 0; f < len; ++f) {
1564 uint8_t b = (uint8_t)res[f];
1566 if ((type&0x02) && f == len-1) b |= 0x80;
1567 emitByte(b);
1569 if (type&0x01) emitByte(0);
1570 } else {
1571 int defined = 1;
1572 int32_t v = getExprArg(&defined);
1574 if (pass > 0 && !defined) fatal("undefined expression");
1575 if (!defined) v = 0; else v += defIncr;
1576 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
1577 if (v < 0) v += 256;
1578 emitByte(v);
1580 if (!skipComma()) break;
1582 return 0;
1586 static int piDEFM (void) { return piDEFSTR(0x00); }
1587 static int piDEFZ (void) { return piDEFSTR(0x01); }
1588 static int piDEFX (void) { return piDEFSTR(0x02); }
1589 static int piDEFC (void) { return piDEFSTR(0x04); }
1592 ///////////////////////////////////////////////////////////////////////////////
1593 // INCBIN
1595 /* INCBIN "name"[,maxlen] */
1596 static int piINCBIN (void) {
1597 int system = 0;
1598 char *fn, qCh;
1599 uint8_t bt;
1600 FILE *fl;
1601 int maxlen = 65536;
1602 char *args = curLine;
1604 if (!curLine[0]) fatal("INCBIN without file name");
1605 if (isStrArg()) {
1606 qCh = *args++;
1607 system = 0;
1608 } else if (curLine[0] == '<') {
1609 qCh = '<'; ++args;
1610 system = 1;
1611 } else {
1612 qCh = 0;
1613 system = 0;
1615 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
1616 if (!fn[0]) fatal("INCBIN: empty file name");
1617 memmove(curLine, args, strlen(args)+1);
1618 // maxlen
1619 if (curLine[0] == ',') {
1620 int defined = 1;
1622 skipComma();
1623 maxlen = getOneExprArg(&defined);
1624 if (!defined) fatal("INCBIN: undefined maxlen");
1625 if (maxlen < 1) return 1; // nothing to do
1627 // now fix name
1628 if (system) {
1629 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
1630 } else {
1631 sprintf(curLine, "%s", fn);
1634 fl = fopen(curLine, "rb");
1635 if (!fl) fatal("INCBIN: file not found: %s", curLine);
1636 while (maxlen-- > 0) {
1637 int res = fread(&bt, 1, 1, fl);
1639 if (!res) break;
1640 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: %s", curLine); }
1641 emitByte(bt);
1643 fclose(fl);
1644 return 1;
1648 ///////////////////////////////////////////////////////////////////////////////
1649 // MODULE, ENDMODULE
1651 static int piENDMODULE (void) {
1652 if (!curModule) fatal("ENDMODULE without MODULE");
1653 if (curLine[0]) {
1654 char *mn = getOneLabelArg();
1656 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
1658 curModule = NULL;
1659 return 0;
1663 static int piMODULE (void) {
1664 ModuleInfo *mi;
1665 char *mn;
1666 SourceLine *ol = curSrcLine;
1667 int inum;
1669 if (curModule) fatal("no nested modules allowed");
1670 mn = getOneLabelArg();
1671 if (!urIsValidLabelName(mn)) fatal("invalid module name: %s", mn);
1672 mi = moduleFind(mn);
1673 //printf("[%s] %p\n", mn, mi);
1674 if (mi) {
1675 if (mi->seen) {
1676 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
1677 /* skip module */
1678 nextSrcLine(); // skip ourself
1679 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
1680 setCurSrcLine(ol);
1681 fatal("no ENDMODULE");
1683 if (inum == 0) fatal("no nested modules allowed");
1684 curModule = mi;
1685 skipInstruction();
1686 piENDMODULE();
1687 return 0;
1689 } else {
1690 mi = moduleAdd(mn, curSrcLine->fname);
1692 mi->seen = 1;
1693 curModule = mi;
1694 return 0;
1698 ///////////////////////////////////////////////////////////////////////////////
1699 // DUP, EDUP
1701 static int piEDUP (void) {
1702 fatal("EDUP without DUP");
1703 checkExprEnd();
1704 return 1;
1708 static int piDUP (void) {
1709 int defined = 1, dupCnt = 1, inum;
1710 SourceLine *stline, *eline = NULL;
1711 int32_t cnt = getOneExprArg(&defined);
1713 if (!defined) fatal("DUP: counter must be defined");
1714 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
1715 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
1716 // now find corresponding EDUP
1717 // note that we should skip nested DUPs
1718 nextSrcLine(); // skip ourself
1719 stline = curSrcLine;
1720 while (curSrcLine) {
1721 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
1722 // ok, we found something; what is it?
1723 if (inum == 0) {
1724 // new DUP, skip it
1725 ++dupCnt;
1726 nextSrcLine(); // skip DUP
1727 } else {
1728 // EDUP
1729 if (--dupCnt == 0) {
1730 // gotcha!
1731 eline = curSrcLine;
1732 break;
1734 nextSrcLine(); // skip EDUP
1737 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
1738 // now repeat that lines
1739 while (cnt-- > 0) {
1740 setCurSrcLine(stline);
1741 while (curSrcLine != eline) processCurrentLine();
1743 return 1;
1747 ///////////////////////////////////////////////////////////////////////////////
1748 // IF, ENDIF
1750 static int ifCount = 0;
1753 static int ifSkipToEndIfOrElse (int wholeBody) {
1754 int inum, wasElse = 0;
1755 SourceLine *oline;
1757 while (curSrcLine) {
1758 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
1759 switch (inum) {
1760 case 0: /* if */
1761 case 6: /* ifx */
1762 nextSrcLine(); // skip IF
1763 ifSkipToEndIfOrElse(1); // and recurse
1764 nextSrcLine(); // skip ENDIF
1765 break;
1766 case 1: /* else */
1767 if (wasElse) fatal("duplicate ELSE");
1768 if (!wholeBody) return 1;
1769 wasElse = 1;
1770 nextSrcLine(); // skip ELSE
1771 break; // and continue
1772 case 2: /* endif */
1773 return 0; // and exit
1774 case 3: /* elif */
1775 case 7: /* elifx */
1776 if (wasElse) fatal("ELSEIF in ELSE");
1777 if (!wholeBody) return 2;
1778 nextSrcLine(); // skip ELSEIF
1779 break; // and continue
1780 case 4: /* macro */
1781 // skip it as a whole
1782 nextSrcLine(); // skip MACRO
1783 for (;;) {
1784 oline = curSrcLine;
1785 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
1786 if (inum == 1) break;
1787 fatal("invalid nested MACRO");
1789 nextSrcLine(); // skip ENDM
1790 break;
1791 case 5: /* endm */
1792 fatal("unexpected ENDM");
1795 fatal("IF without ENDIF");
1796 return -1;
1800 static int piENDIF (void) {
1801 if (--ifCount < 0) fatal("ENDIF without IF");
1802 checkExprEnd();
1803 return 1;
1807 static int piELSE (void) {
1808 if (--ifCount < 0) fatal("ELSE without IF");
1809 nextSrcLine(); // skip ELSE
1810 ifSkipToEndIfOrElse(1);
1811 return 1;
1815 static int piELSEIF (void) {
1816 if (--ifCount < 0) fatal("ELSEIF without IF");
1817 nextSrcLine(); // skip ELSEIF
1818 ifSkipToEndIfOrElse(1);
1819 return 1;
1823 static int piIFAll (int isIfX) {
1824 int defined = 1;
1825 int ooo = lblOptMakeU2;
1826 lblOptMakeU2 = isIfX ? 1 : 0;
1827 int32_t cond = getOneExprArg(&defined);
1828 lblOptMakeU2 = ooo;
1830 if (!defined) {
1831 if (!isIfX) fatal("IF: condition must be defined");
1832 cond = 0; // for IFX: 0 is there is any undefined label
1834 if (cond) {
1835 // ok, do it until ELSE/ELSEIF/ENDIF
1836 ++ifCount;
1837 return 1;
1839 for (;;) {
1840 int r;
1841 char *args;
1843 nextSrcLine(); // skip last instruction
1844 // skip until ELSE/ELSEIF/ENDIF
1845 r = ifSkipToEndIfOrElse(0);
1846 if (r == 0) break; // ENDIF
1847 if (r == 1) { ++ifCount; break; } // ELSE
1848 // ELSEIF, do condition
1849 if ((args = strIsCommand("ELSEIF", curLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
1850 memmove(curLine, args, strlen(args)+1);
1851 cond = getOneExprArg(&defined);
1852 if (!defined) fatal("ELSEIF: condition must be defined");
1853 if (cond) { ++ifCount; break; } // condition is true
1855 return 1;
1859 static int piIF (void) { return piIFAll(0); }
1860 static int piIFX (void) { return piIFAll(1); }
1863 ///////////////////////////////////////////////////////////////////////////////
1864 // macro processor
1866 * what i did with MACRO is the brain-damaged cheating all the way.
1867 * first, i will collect the MACRO body and remember it.
1868 * second, when the macro is used, i will:
1869 * * create unique labels for all supplied macro args, each with
1870 * number (so IFARG/IFNARG can check the existance)
1871 * * insert the whole macro body in place, fixing argument refs
1872 * * let the asm play with it
1873 * another tricky part is 'local labels': i have to change all
1874 * '..lbl' references -- generate new label name for it and
1875 * replace all refs. and be careful to not touch the strings.
1876 * this is not the best scheme, but it is fairly simple and it wokrs.
1878 static int piMACRO (void) {
1879 fatal("sorry, no MACRO yet");
1880 return 1;
1884 static int piENDM (void) {
1885 fatal("ENDM withoud MACRO");
1886 return 1;
1890 ///////////////////////////////////////////////////////////////////////////////
1891 // line processor
1892 static int optWriteType = 't';
1893 static int optWTChanged = 0;
1896 static void piTapParseLoaderName (void) {
1897 if (skipComma()) {
1898 int len;
1899 char *fn;
1901 if (!isStrArg()) fatal("loader name expected");
1902 fn = getStrArg(&len);
1903 if (len > 10) fatal("loader name too long");
1904 memset(tapeLoaderName, ' ', 10);
1905 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
1910 static int piDEFFMT (void) {
1911 char *name;
1913 if (isStrArg()) {
1914 int len = 0;
1916 name = getStrArg(&len);
1917 } else {
1918 name = getLabelArg();
1920 if (optWTChanged) return 1;
1921 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA")) {
1922 optRunTape = 0;
1923 //optRunSNA = (toupper(name[0]) == 'R');
1924 optWriteType = 's';
1925 if (curLine[0]) fatal("too many expressions");
1926 return 1;
1928 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
1929 optRunTape = 0;
1930 //optRunSNA = 0;
1931 optWriteType = 't';
1932 piTapParseLoaderName();
1933 return 1;
1935 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE")) {
1936 optRunTape = 1;
1937 //optRunSNA = 0;
1938 optWriteType = 't';
1939 piTapParseLoaderName();
1940 return 1;
1942 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
1943 optRunTape = 0;
1944 //optRunSNA = 0;
1945 optWriteType = 'r';
1946 if (curLine[0]) fatal("too many expressions");
1947 return 1;
1949 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB")) {
1950 optRunTape = 0;
1951 //optRunSNA = 0;
1952 optRunDMB = (toupper(name[0]) == 'R');
1953 optWriteType = 'd';
1954 if (curLine[0]) fatal("too many expressions");
1955 return 1;
1957 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
1958 optRunTape = 0;
1959 //optRunSNA = 0;
1960 optWriteType = 'n';
1961 if (curLine[0]) fatal("too many expressions");
1962 return 1;
1964 fatal("invalid default output type: %s", name);
1968 ///////////////////////////////////////////////////////////////////////////////
1969 // line processor
1971 static void processCurrentLine (void) {
1972 if (!curSrcLine) return; // do nothing
1973 loadCurSrcLine();
1974 processLabel();
1975 for (;;) {
1976 char *str, *ee, name[66];
1977 UrAsmOp *op;
1978 int len;
1979 const char *errpos;
1981 removeSpacesAndColons();
1982 // skip spaces and ':'
1983 if (!curLine[0]) { nextSrcLine(); break; }
1984 // try to find and process command
1985 str = curLine; //while (*str && isspace(*str)) ++str; // skip spaces
1986 // find command end
1987 for (ee = str; *ee && !isspace(*ee) && *ee != '"' && *ee != '\''; ++ee) ;
1988 // get command, if any
1989 if (ee != str && ee-str <= 64) {
1990 memset(name, 0, sizeof(name));
1991 memmove(name, str, ee-str);
1992 // known command?
1993 op = urFindOp(name);
1994 if (op) {
1995 // ok, do it
1996 str = ee; while (*str && isspace(*str)) ++str; // skip spaces
1997 memmove(curLine, str, strlen(str)+1);
1998 if (op->fn()) {
1999 nextSrcLine(); // skip it
2000 break;
2005 len = urAssembleOne(curLine, pc, disp, &errpos);
2006 if (len < 0) fatalUrLib(len);
2007 pc += len; disp += len;
2008 if (len >= 0 && errpos) {
2009 memmove(curLine, errpos+1, strlen(errpos));
2010 } else {
2011 nextSrcLine(); // skip it
2012 break;
2018 ///////////////////////////////////////////////////////////////////////////////
2019 // setup instructions
2021 static void registerInstructions (void) {
2022 urAddOp("DISPLAY", piDISPLAY);
2023 urAddOp("DISPLAY0", piDISPLAY0);
2024 urAddOp("DISPLAYA", piDISPLAYA);
2025 urAddOp("DISPHEX", piDISPHEX);
2026 urAddOp("DISPHEX0", piDISPHEX0);
2027 urAddOp("DISPHEXA", piDISPHEXA);
2029 urAddOp("DEFFMT", piDEFFMT);
2031 urAddOp("MACRO", piMACRO);
2032 urAddOp("ENDM", piENDM);
2034 urAddOp("ORG", piORG);
2035 urAddOp("DISP", piDISP);
2036 urAddOp("ENDDISP", piENDDISP);
2037 urAddOp("PHASE", piDISP);
2038 urAddOp("DEPHASE", piENDDISP);
2039 urAddOp("UNPHASE", piENDDISP);
2040 urAddOp("ALIGN", piALIGN);
2041 urAddOp("DISPALIGN", piDISPALIGN);
2042 urAddOp("PHASEALIGN", piDISPALIGN);
2043 urAddOp("ENT", piENT);
2044 urAddOp("CLR", piCLR);
2045 urAddOp("RESERVE", piRESERVE);
2047 urAddOp("INCBIN", piINCBIN);
2049 urAddOp("MODULE", piMODULE);
2050 urAddOp("ENDMODULE", piENDMODULE);
2052 urAddOp("DUP", piDUP);
2053 urAddOp("EDUP", piEDUP);
2055 urAddOp("IF", piIF);
2056 urAddOp("IFX", piIFX);
2057 urAddOp("ELSE", piELSE);
2058 urAddOp("ELSEIF", piELSEIF);
2059 urAddOp("ELSEIFX", piELSEIF);
2060 urAddOp("ENDIF", piENDIF);
2062 urAddOp("DEFINCR", piDEFINCR);
2063 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
2064 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
2065 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
2066 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
2067 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
2068 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
2069 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
2070 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
2074 ///////////////////////////////////////////////////////////////////////////////
2075 // !0: invalid label
2077 static inline void fnSkipSpaces (const char *expr) {
2078 while (*expr && isspace(*expr)) ++expr;
2079 return expr;
2084 static inline char fnNextChar (const char *expr) {
2085 while (*expr && isspace(*expr)) ++expr;
2086 return *expr;
2090 #define FN_CHECK_END do { \
2091 while (*expr && isspace(*expr)) ++expr; \
2092 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
2093 ++expr; \
2094 } while (0)
2097 #define FN_CHECK_COMMA do { \
2098 while (*expr && isspace(*expr)) ++expr; \
2099 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
2100 ++expr; \
2101 } while (0)
2104 static const char *readLabelName (char *buf, const char *expr) {
2105 int pos = 0;
2107 while (*expr && isspace(*expr)) ++expr;
2108 for (;;) {
2109 char ch = *expr++;
2111 if (pos >= 128) return NULL;
2112 if (!ch) break;
2113 if (isalnum(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
2114 buf[pos++] = ch;
2115 } else {
2116 break;
2119 if (pos < 1) return NULL;
2120 buf[pos] = '\0';
2121 if (!urIsValidLabelName(buf)) return NULL;
2122 return expr;
2126 static const char *fnDefKn (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
2127 char lbl[130];
2129 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
2130 FN_CHECK_END;
2131 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
2132 return expr;
2135 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); }
2136 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); }
2139 static const char *fnAligned256 (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2140 expr = urExpressionEx(res, expr, addr, &donteval, defined, error);
2141 FN_CHECK_END;
2142 if (!donteval) res->val = (res->val%256 ? 0 : 1);
2143 return expr;
2147 static const char *fnSameSeg (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2148 URExprValue v0, v1;
2150 urInitExprValue(&v0);
2151 urInitExprValue(&v1);
2152 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2153 FN_CHECK_COMMA;
2154 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2155 FN_CHECK_END;
2156 if (!donteval) res->val = (v0.val/256 == v1.val/256);
2157 return expr;
2161 static const char *fnAlign (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2162 URExprValue v0, v1;
2164 urInitExprValue(&v0);
2165 urInitExprValue(&v1);
2166 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2167 if (fnNextChar(expr) == ',') {
2168 FN_CHECK_COMMA;
2169 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2170 } else {
2171 v1.val = 256;
2173 FN_CHECK_END;
2174 if (!donteval) {
2175 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
2176 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
2178 return expr;
2182 static void registerFunctions (void) {
2183 urExpressionRegisterFunction("defined", fnDefined);
2184 urExpressionRegisterFunction("known", fnKnown);
2185 urExpressionRegisterFunction("aligned256", fnAligned256);
2186 urExpressionRegisterFunction("align", fnAlign);
2187 urExpressionRegisterFunction("sameseg", fnSameSeg);
2191 ///////////////////////////////////////////////////////////////////////////////
2192 // preparing another pass
2194 static void initPass (void) {
2195 curSrcLine = asmText;
2196 curModule = NULL;
2197 pc = disp = ent = 0x100; // viva CP/M!
2198 inTapeBlock = 0;
2199 tapeXorB = 0;
2200 wasOrg = 0;
2201 wasClr = 0;
2202 ifCount = 0;
2203 defIncr = 0;
2204 lblOptMakeU2 = 0;
2205 lastSeenGlobalLabel = strdup(" [MAIN] ");
2206 modulesResetSeen();
2207 prepareMemory();
2211 static int posstPass (void) {
2212 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
2213 if (checkLabels()) return -1;
2214 if (ifCount != 0) fatal("unbalanced IFs");
2215 return 0;
2219 ///////////////////////////////////////////////////////////////////////////////
2220 // options
2222 static struct option longOpts[] = {
2223 {"sna", 0, NULL, 's'},
2224 {"sna128", 0, NULL, 'S'},
2225 {"tap", 0, NULL, 't'},
2226 {"autotap", 0, NULL, 'T'},
2227 /*{"rawtap", 0, NULL, 'T'},*/
2228 {"raw", 0, NULL, 'r'},
2229 {"autodmb", 0, NULL, 'B'},
2230 {"dmb", 0, NULL, 'b'},
2231 {"none", 0, NULL, 'n'},
2232 {"help", 0, NULL, 'h'},
2233 {NULL, 0, NULL, 0}
2237 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
2240 static void usage (const char *pname) {
2241 printf(
2242 "usage: %s [options] infile\n"
2243 "default infiles:", pname);
2244 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
2245 printf("\n"
2246 "options:\n"
2247 " -s --sna write 48K .SNA file with autostart\n"
2248 " -S --sna128 write 148K .SNA file with autostart\n"
2249 " -t --tap write .tap file\n"
2250 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
2251 " -r --raw write raw file(s)\n"
2252 " -b --dmb write DMB file\n"
2253 " -B --autodmb write DMB file with autostart\n"
2254 " -n --none write nothing\n"
2255 " -h --help this help\n");
2259 ///////////////////////////////////////////////////////////////////////////////
2260 // main
2262 int main (int argc, char *argv[]) {
2263 int res = 0, c;
2264 const char *pname = argv[0];
2265 char *inFile = NULL;
2266 initInclideDir();
2268 urGetByteFn = getByte;
2269 urPutByteFn = putByte;
2270 urFindLabelByNameFn = findLabelCB;
2271 //urIsLabelDefinedOrKnownFn = isLabelDefinedOrKnown;
2273 //strcpy(tapeLoaderName, "cargador ");
2274 tapeLoaderName[0] = 0;
2276 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
2277 while ((c = getopt_long(argc, argv, "sStTbBrnh", longOpts, NULL)) >= 0) {
2278 switch (c) {
2279 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
2280 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
2281 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
2282 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
2283 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
2284 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
2285 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
2286 case 'h': usage(pname); res = 0; goto earlyerrquit;
2287 case '?': return 1;
2290 if (optind >= argc) {
2291 // try to find default input file
2292 int f;
2293 for (f = 0; defInFiles[f]; ++f) {
2294 if (!access(defInFiles[f], R_OK)) {
2295 inFile = strdup(defInFiles[f]);
2296 break;
2299 } else {
2300 inFile = strdup(argv[optind]);
2302 if (!inFile || !inFile[0]) {
2303 res = 1;
2304 fprintf(stderr, "ERROR: no input file!\n");
2305 goto earlyerrquit;
2308 registerInstructions();
2309 registerFunctions();
2311 res = asmTextLoad(inFile);
2312 if (!res) {
2314 printf("dumping...\n");
2315 FILE *fo = fopen("z000.out", "w");
2316 if (fo) {
2317 SourceLine *c;
2318 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
2319 fclose(fo);
2322 for (pass = 0; pass <= 1; ++pass) {
2323 initPass();
2324 printf("pass %d\n", pass);
2325 setCurSrcLine(asmText);
2326 if (setjmp(errJP)) { res = 1; break; }
2327 while (curSrcLine) processCurrentLine();
2328 if (posstPass()) { res = 1; break; }
2330 // write result
2331 if (res == 0) {
2332 char *oc = strdup(inFile);
2333 char *pd = strrchr(oc, '.');
2334 if (pd && !strchr(oc, '/')) *pd = '\0';
2335 switch (optWriteType) {
2336 case 's': saveSna(oc, optSNA48); break;
2337 case 't': saveTap(oc); break;
2338 case 'r': saveRaw(oc); break;
2339 case 'd': saveDMB(oc); break;
2341 free(oc);
2343 } else {
2344 fprintf(stderr, "ERROR: loading error!\n");
2347 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
2348 urClearLabels();
2349 modulesClear();
2350 asmTextClear();
2351 urClearOps();
2352 earlyerrquit:
2353 if (inFile) free(inFile);
2354 if (sysIncludeDir) free(sysIncludeDir);
2355 return res?1:0;