urasm: cosmetix
[urasm.git] / src / urasm.c
blob2a497f9e768408147ebbe40dfb244e9606d7bf09
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 /*TODO: expression parser should understant escapes in strings!*/
67 /* trim trailing spaces and comments */
68 static void normalizeStr (char *s) {
69 char *p, inQ = 0;
70 int f;
71 // now skip all shit
72 for (p = s; *p; ++p) {
73 if (inQ) {
74 // inside the string
75 if (*p == '\\') {
76 switch (*(++p)) {
77 case 'x': case 'X': // hex code
78 ++p; // skip 'x'
79 for (f = 0; f < 2; ++f) if (!isxdigit(*p)) break;
80 --p; // last digit will be skiped by 'for'
81 break;
82 case '0': // octal code
83 for (f = 0; f < 4; ++f) if (!isdigit(*p) || *p > '7') break;
84 --p; // last digit will be skiped by 'for'
85 break;
86 case '1' ... '9': // decimal code
87 for (f = 0; f < 3; ++f) if (!isdigit(*p)) break;
88 --p; // last digit will be skiped by 'for'
89 break;
90 default: ; // other escapes, do nothing
92 } else {
93 if (*p == inQ) inQ = 0;
95 continue;
97 // outside the string
98 switch (*p) {
99 case '"': case '\'': // string catched
100 inQ = *p;
101 break;
102 case ';': // end of line, comment follows
103 *p-- = '\0'; // strip it and quit
104 break;
105 default: ; // do nothing
108 // trim right
109 for (p = s+strlen(s)-1; p >= s && isspace(*p); --p) ;
110 p[1] = '\0';
114 /* returns NULL or pointer to args */
115 /* skips spaces after command if any */
116 static char *strIsCommand (const char *command, char *str) {
117 int cnt;
119 for (cnt = 1; cnt > 0; --cnt) {
120 while (*str && isspace(*str)) ++str; // skip spaces
121 for (; *command && *str; ++command, ++str) {
122 if (toupper(*command) != toupper(*str)) return NULL; // alas
124 if (*command) return NULL; // alas
125 if (*str && isalnum(*str)) return NULL; // alas
126 while (*str && isspace(*str)) ++str; // skip spaces
127 if (*str && *str == ':') break; // try again if we have a colon
128 return str; // found
130 return NULL;
134 /* don't free() result */
135 /* skips trailing spaces */
136 static char *parseStr (char **str, char endQ, int *lenp) {
137 static char buf[MAX_LINE_SIZE];
138 int len = 0, n, f, base;
139 char *a = *str;
141 int xDigit (char ch, int base) {
142 if (ch < '0') return -1;
143 if (base <= 10) {
144 if (ch >= '0'+base) return -1;
145 return ch-'0';
147 ch = toupper(ch);
148 if (ch <= '9') return ch-'0';
149 if (ch < 'A' || ch > 'A'+base-10) return -1;
150 ch -= 'A'-10;
151 return ch<base ? ch : -1;
154 memset(buf, 0, sizeof(buf));
155 if (lenp) *lenp = 0;
156 for (; *a; ++a) {
157 if (*a == '\\') {
158 if (!a[1]) break;
159 switch (*(++a)) {
160 case 'a': buf[len++] = '\a'; break;
161 case 'b': buf[len++] = '\b'; break;
162 case 'f': buf[len++] = '\f'; break;
163 case 'n': buf[len++] = '\n'; break;
164 case 'r': buf[len++] = '\r'; break;
165 case 't': buf[len++] = '\t'; break;
166 case 'v': buf[len++] = '\v'; break;
167 case 'z': buf[len++] = '\0'; break;
168 case 'x': case 'X': // hex
169 ++a; // skip 'x'
170 base = 16; f = 2;
171 donum: for (n = 0; f > 0; --f) {
172 char ch = xDigit(*a++, base);
173 if (ch < 0) { --a; break; }
174 n *= base;
175 n += ch;
177 buf[len++] = n;
178 --a; // return to the last digit, 'for' will skip it
179 break;
180 case '0': // octal
181 base = 8; f = 4;
182 goto donum;
183 case '1' ... '9': // decimal
184 base = 10; f = 3;
185 goto donum;
186 default: buf[len++] = a[0]; break; // others
188 } else {
189 if (*a == endQ) { ++a; break; }
190 buf[len++] = *a;
193 while (*a && isspace(*a)) ++a; // skip trailing spaces
194 *str = a;
195 buf[len] = '\0';
196 if (lenp) *lenp = len;
197 return buf;
201 ///////////////////////////////////////////////////////////////////////////////
202 // source file stack, reader, etc
204 typedef struct SourceLine SourceLine;
205 struct SourceLine {
206 SourceLine *next;
207 char *line;
208 char *fname;
209 int lineNo;
212 static SourceLine *asmText = NULL;
213 static SourceLine *asmTextLast = NULL;
214 static SourceLine *curSrcLine = NULL;
217 static void asmTextClear (void) {
218 while (asmText) {
219 SourceLine *l = asmText;
221 asmText = asmText->next;
222 free(l->line);
223 free(l->fname);
224 free(l);
226 asmTextLast = curSrcLine = NULL;
230 static void normIncName (char *dest, const char *fn, int system) {
231 struct stat st;
233 if (system) sprintf(dest, "%s/%s", sysIncludeDir, fn); else sprintf(dest, "%s", fn);
234 if (stat(dest, &st)) return;
235 if (S_ISDIR(st.st_mode)) strcat(dest, "/zzmain.zas");
239 static int asmTextLoad (const char *fname) {
240 FILE *fl;
241 int lineNo = 0;
242 char *args;
243 SourceLine *s;
244 static int includeCount = 0;
246 if (!(fl = fopen(fname, "r"))) {
247 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
248 return -1;
250 printf("loading: %s\n", fname);
251 // read file
252 while (fgets(curLine, sizeof(curLine)-1, fl)) {
253 ++lineNo;
254 curLine[sizeof(curLine)-1] = '\0';
255 normalizeStr(curLine);
256 if (!curLine[0]) continue; // don't store empty lines
257 // find specials, if any
258 if (isspace(curLine[0])) {
259 if ((args = strIsCommand("INCLUDE", curLine)) != NULL) {
260 // process 'INCLUDE'
261 int system = 0;
262 char qCh = 0;
264 if (!args[0]) {
265 fclose(fl);
266 fprintf(stderr, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname, lineNo);
267 return -1;
269 if (args[0] == '"' || args[0] == '\'') qCh = *args++;
270 else if (args[0] == '<') { qCh = '>'; ++args; system = 1; }
271 char *fn = parseStr(&args, qCh, NULL); // i don't care about string length here
272 if (!fn[0]) {
273 fclose(fl);
274 fprintf(stderr, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname, lineNo);
275 return -1;
277 if (args[0]) {
278 fclose(fl);
279 fprintf(stderr, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname, lineNo);
280 return -1;
282 if (includeCount > 256) {
283 fclose(fl);
284 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname, lineNo);
285 return -1;
287 normIncName(curLine, fn, system);
288 //if (system) sprintf(curLine, "%s/%s", sysIncludeDir, fn); else sprintf(curLine, "%s", fn);
289 fn = strdup(curLine); if (!fn) abort();
290 ++includeCount;
291 system = asmTextLoad(fn);
292 --includeCount;
293 free(fn);
294 if (system) { fclose(fl); return system; }
295 continue;
298 // add current line
299 s = calloc(1, sizeof(SourceLine));
300 if (!s) abort();
301 s->lineNo = lineNo;
302 s->line = strdup(curLine); if (!s->line) abort();
303 s->fname = strdup(fname); if (!s->fname) abort();
304 if (asmTextLast) asmTextLast->next = s; else asmText = s;
305 asmTextLast = s;
307 fclose(fl);
308 return 0;
312 static inline void loadCurSrcLine (void) { if (curSrcLine) strcpy(curLine, (curSrcLine != NULL ? curSrcLine->line : "")); }
313 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
314 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
317 ///////////////////////////////////////////////////////////////////////////////
318 // prototypes
320 static void processCurrentLine (void); // only one, will skip to next one
323 ///////////////////////////////////////////////////////////////////////////////
324 // error raisers, etc
326 static jmp_buf errJP;
329 static void errorWriteFile (void) {
330 if (curSrcLine) {
331 fprintf(stderr, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
332 } else {
333 fprintf(stderr, "somewhere in time: ");
337 static void errorMsgV (const char *fmt, va_list ap) {
338 errorWriteFile();
339 vfprintf(stderr, fmt, ap);
340 va_end(ap);
341 fputc('\n', stderr);
342 fflush(stderr);
346 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
347 va_list ap;
348 fprintf(stderr, "WARNING ");
349 va_start(ap, fmt);
350 errorMsgV(fmt, ap);
354 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
355 va_list ap;
356 fprintf(stderr, "FATAL ");
357 va_start(ap, fmt);
358 errorMsgV(fmt, ap);
362 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
363 va_list ap;
364 va_start(ap, fmt);
365 errorMsgV(fmt, ap);
366 longjmp(errJP, 666);
370 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
371 errorMsg("%s", urErrorMessage(errcode));
372 longjmp(errJP, 666);
376 //////////////////////////////////////////////////////////////////////////////
377 // operator management
379 typedef int (*UrAsmOpFn) (void);
381 typedef struct UrAsmOp {
382 char *name;
383 UrAsmOpFn fn;
384 struct UrAsmOp *next;
385 } UrAsmOp;
387 static UrAsmOp *oplist = NULL;
390 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
391 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
392 if (!res) abort();
393 res->name = strdup(name);
394 res->fn = fn;
395 res->next = oplist;
396 oplist = res;
397 return res;
401 static UrAsmOp *urFindOp (const char *name) {
402 UrAsmOp *res;
403 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
404 return res;
408 static void urClearOps (void) {
409 UrAsmOp *c;
410 while (oplist) {
411 c = oplist; oplist = oplist->next;
412 free(c->name);
413 free(c);
418 ///////////////////////////////////////////////////////////////////////////////
419 // label management
421 typedef struct UrLabelInfo UrLabelInfo;
422 struct UrLabelInfo {
423 char *name;
424 int32_t value;
425 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
426 int known; /* !0: label value already known */
427 int refLine; /* first referenced line */
428 char *refFile;
429 UrLabelInfo *next;
432 static UrLabelInfo *labels = NULL;
434 static void urClearLabels (void) {
435 UrLabelInfo *c;
436 while ((c = labels)) {
437 labels = labels->next;
438 if (c->name) free(c->name);
439 if (c->refFile) free(c->refFile);
440 free(c);
445 static UrLabelInfo *urFindLabel (const char *name) {
446 UrLabelInfo *c;
447 for (c = labels; c; c = c->next) if (!strcmp(name, c->name)) break;
448 return c;
452 static UrLabelInfo *urAddLabel (const char *name) {
453 UrLabelInfo *c = urFindLabel(name);
454 if (!c) {
455 UrLabelInfo *p;
456 for (p = NULL, c = labels; c; p = c, c = c->next) ;
457 c = calloc(1, sizeof(UrLabelInfo));
458 if (!c) abort();
459 c->name = strdup(name);
460 c->type = -1;
461 if (p) p->next = c; else labels = c;
462 c->next = NULL;
464 return c;
468 ///////////////////////////////////////////////////////////////////////////////
469 // module list management
471 typedef struct ModuleInfo ModuleInfo;
472 struct ModuleInfo {
473 char *name;
474 char *fname; // opened in this file
475 int seen; // !0: module already seen, skip other definitions from the same file
476 ModuleInfo *next;
478 static ModuleInfo *modules = NULL;
479 static ModuleInfo *curModule = NULL;
482 static void modulesClear (void) {
483 curModule = NULL;
484 while (modules) {
485 ModuleInfo *c = modules;
486 modules = modules->next;
487 free(c->name);
488 free(c->fname);
489 free(c);
494 static void modulesResetSeen (void) {
495 ModuleInfo *c;
496 for (c = modules; c; c = c->next) c->seen = 0;
500 static ModuleInfo *moduleFind (const char *name) {
501 ModuleInfo *c;
502 if (!name || !name[0]) return NULL;
503 for (c = modules; c; c = c->next) if (!strcmp(c->name, name)) break;
504 return c;
508 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
509 ModuleInfo *c;
510 if (!name || !fname || !name[0] || !fname[0]) abort();
511 c = calloc(1, sizeof(ModuleInfo));
512 if (!c) abort();
513 c->name = strdup(name); if (!c->name) abort();
514 c->fname = strdup(fname); if (!c->fname) abort();
515 c->next = modules;
516 modules = c;
517 return c;
521 ///////////////////////////////////////////////////////////////////////////////
522 // destination memory management
524 static uint8_t memory[65536];
525 static char memused[65536];
526 static char memresv[65536];
527 static uint16_t pc = 0; /* current position to write */
528 static uint16_t disp = 0; /* current 'virtual PC' */
529 static uint16_t ent = 0; /* starting address */
530 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
531 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
532 static int inTapeBlock = 0;
533 static uint8_t tapeXorB = 0;
536 static inline uint8_t getByte (uint16_t addr) {
537 return memory[addr];
541 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
542 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
546 static inline void putByte (uint16_t addr, uint8_t b) {
547 if (inTapeBlock) tapeXorB ^= b;
548 memory[addr] = b;
549 memused[addr] = 1;
553 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
554 putByte(addr, w&0xFFU);
555 putByte(addr+1, (w>>8)&0xFFU);
559 static inline void emitByte (uint8_t b) {
560 putByte(pc, b);
561 ++pc;
562 ++disp;
566 static inline void emitWord (uint16_t w) {
567 emitByte(w&0xFFU);
568 emitByte((w>>8)&0xFFU);
572 static inline void emitRWord (uint16_t w) {
573 emitByte((w>>8)&0xFFU);
574 emitByte(w&0xFFU);
578 static void prepareMemory (void) {
579 memset(memory, 0, sizeof(memory));
580 memset(memused, 0, sizeof(memused));
581 memset(memresv, 0, sizeof(memresv));
585 ///////////////////////////////////////////////////////////////////////////////
586 // label getter and utilities
588 static char *lastSeenGlobalLabel = NULL; /* global */
591 static char *fixLocalLabel (const char *name) {
592 static char newname[MAX_LINE_SIZE*2+1024];
594 memset(newname, 0, sizeof(newname));
595 if (!name || !name[0]) newname[0] = '\0';
596 else if (!lastSeenGlobalLabel || name[0] != '.') strcpy(newname, name);
597 else {
598 // this is local label, let's rename it
599 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
601 return newname;
605 static char *fixGlobalLabel (const char *name) {
606 static char newname[MAX_LINE_SIZE*2+1024];
608 memset(newname, 0, sizeof(newname));
609 if (!name || !name[0]) newname[0] = '\0';
610 else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
611 if (name[0] == '@' && name[1]) ++name;
612 strcpy(newname, name);
613 } else {
614 // this is global unqualified label and we have a module; let's rename it
615 sprintf(newname, "%s.%s", curModule->name, name);
617 //printf("%s --> %s\n", name, newname);
618 return newname;
622 static int lblOptMakeU2 = 0;
623 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found) {
624 UrLabelInfo *lbl;
625 char *ln, *nn;
627 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
628 lbl = urFindLabel(nn);
629 if (!lbl) {
630 // try non-module label
631 lbl = urFindLabel(ln);
633 if (!lbl) {
634 if (pass != 0) {
635 errorMsg("using undefined label %s", ln);
636 *found = 0;
637 *defined = 0;
638 return 0;
640 lbl = urAddLabel(nn);
641 lbl->type = lblOptMakeU2 ? -42 : -1;
642 lbl->known = 0;
643 lbl->refLine = curSrcLine->lineNo;
644 lbl->refFile = strdup(curSrcLine->fname);
645 //printf("new label: [%s]\n", lbl->name);
646 } else {
647 //printf("label reference: [%s]\n", lbl->name);
649 if (lbl) {
650 *found = 1;
651 *defined = lbl->known!=0;
652 return lbl->value;
654 *found = 0;
655 *defined = 0;
656 return 0;
660 // qtypes
661 enum {
662 UR_QTYPE_DEFINED,
663 UR_QTYPE_KNOWN
667 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
668 UrLabelInfo *lbl;
669 char *ln, *nn;
671 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
672 lbl = urFindLabel(nn);
673 if (!lbl) {
674 // try non-module label
675 lbl = urFindLabel(ln);
677 switch (qtype) {
678 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
679 case UR_QTYPE_KNOWN: return lbl!=NULL;
680 default: ;
682 return 0;
686 static int checkLabels (void) {
687 UrLabelInfo *c;
688 int wasError = 0;
690 for (c = labels; c; c = c->next) {
691 if (c->type == -1) {
692 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
693 wasError = 1;
695 if (c->type == 0) c->known = -1;
697 //if (wasError) longjmp(errJP, 667);
698 return wasError;
702 ///////////////////////////////////////////////////////////////////////////////
703 // expression utils
705 static int32_t getExprArg (int *defined) {
706 int error = 0;
707 char *a = curLine;
708 const char *ee;
709 int32_t res;
711 while (*a && isspace(*a)) ++a;
712 if (!a[0]) fatal("expression expected");
713 ee = urExpression(&res, a, disp, defined, &error);
714 if (error) fatalUrLib(error);
715 if (*ee) {
716 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
717 memmove(curLine, ee, strlen(ee)+1);
718 } else {
719 curLine[0] = '\0';
721 return res;
725 static int32_t getOneExprArg (int *defined) {
726 int32_t res = getExprArg(defined);
728 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
729 return res;
733 static int isStrArg (void) { return (curLine[0] == '"' || curLine[0] == '\''); }
736 static char *getStrArg (int *lenp) {
737 char *res, qCh;
738 char *a = curLine;
740 while (*a && isspace(*a)) ++a;
741 qCh = *a++;
742 if (qCh != '"' && qCh != '\'') fatal("string expected");
743 res = parseStr(&a, qCh, lenp);
744 if (*a) {
745 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
746 memmove(curLine, a, strlen(a)+1);
747 } else {
748 curLine[0] = '\0';
750 return res;
754 static MAYBE_UNUSED char *getOneStrArg (int *lenp) {
755 char *res = getStrArg(lenp);
757 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
758 return res;
762 static char *getLabelArg (void) {
763 static char res[MAX_LINE_SIZE+128], *p;
764 char *a = curLine;
766 memset(res, 0, sizeof(res));
767 while (*a && isspace(*a)) ++a;
768 if (!a[0]) fatal("label expected");
769 for (p = res; *a && *a != ','; ++a, ++p) *p = *a;
770 for (; p > res && isspace(p[-1]); --p) ;
771 *p = '\0';
772 if (p-res > 120) fatal("label name too long: %s", res);
773 while (*a && isspace(*a)) ++a;
774 if (*a) {
775 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
776 memmove(curLine, a, strlen(a)+1);
777 } else {
778 curLine[0] = '\0';
780 return res;
784 static char *getOneLabelArg (void) {
785 char *res = getLabelArg();
787 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
788 return res;
792 /* res == 0: end of expression */
793 static int skipComma (void) {
794 char *a;
796 for (a = curLine; *a && isspace(*a); ++a) ;
797 if (!a[0]) { curLine[0] = '\0'; return 0; }
798 if (a[0] == ':') {
799 while (a[0] && (a[0] == ':' || isspace(a[0]))) ++a;
800 if (!a[0]) { curLine[0] = '\0'; return 0; }
801 memmove(curLine, a, strlen(a)+1);
802 return -1;
804 if (a[0] != ',') fatal("invalid expression: ',' expected");
805 for (++a; *a && isspace(*a); ++a) ;
806 if (!a[0]) { curLine[0] = '\0'; return 0; }
807 memmove(curLine, a, strlen(a)+1);
808 return 1;
812 static void skipInstruction (void) {
813 char *str = curLine;
814 int cnt;
816 for (cnt = 1; cnt > 0; --cnt) {
817 while (*str && isspace(*str)) ++str; // skip spaces
818 while (*str && !isspace(*str)) ++str; // skip non-spaces
819 while (*str && isspace(*str)) ++str; // skip spaces
820 if (!str[0] || *str != ':') break;
821 // try again if we have a colon
823 memmove(curLine, str, strlen(str)+1);
827 ///////////////////////////////////////////////////////////////////////////////
828 // label processor
830 static MAYBE_UNUSED void removeSpacesAndColons (void) {
831 char *ep = curLine;
833 while (*ep && (isspace(*ep) || *ep == ':')) ++ep;
834 memmove(curLine, ep, strlen(ep)+1);
838 static void checkExprEnd (void) {
839 char *ep = curLine;
841 while (*ep && isspace(*ep)) ++ep;
842 memmove(curLine, ep, strlen(ep)+1);
843 if (curLine[0] && curLine[0] != ':') fatal("end of expression expected");
847 /* remove label from curLine */
848 static void removeLabel (void) {
849 char *ep = curLine;
851 if (ep[0] && !isspace(ep[0])) for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ; // skip text
852 // skip spaces and colons
853 while (*ep && (isspace(*ep) || *ep == ':')) ++ep;
854 memmove(curLine, ep, strlen(ep)+1);
858 static void processLabel (void) {
859 char *argstart;
860 char *ep, *ln, *nn;
861 static char n2[256];
862 UrLabelInfo *lbl;
863 int noLocAff = 0, doEQU = 0;
865 memset(n2, 0, sizeof(n2));
866 if (!curLine[0] || isspace(curLine[0]) || curLine[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
867 // collect label
868 for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ;
869 if (ep-curLine > 120) fatal("label too long");
870 // copy label
871 memset(n2, 0, sizeof(n2));
872 memmove(n2, curLine, ep-curLine);
873 if (!urIsValidLabelName(n2)) return; // this is not a valid label, get out of here
874 // check if this can be instruction
875 while (*ep && isspace(*ep)) ++ep;
876 if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
877 // ok, we got a good label
878 removeLabel();
879 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
880 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
881 lbl = urAddLabel(nn);
882 if (!lbl->refFile) {
883 lbl->refLine = curSrcLine->lineNo;
884 lbl->refFile = strdup(curSrcLine->fname);
886 //printf("new: [%s]\n", lbl->name);
887 // get command name
888 if (curLine[0] == '=') {
889 doEQU = 0;
890 argstart = curLine+1;
891 } else {
892 doEQU = 1;
893 argstart = strIsCommand("EQU", curLine);
895 if (!argstart || doEQU) {
896 if (pass == 0 && lbl->type != -1) fatal("duplicate label %s", lbl->name);
898 if (argstart) {
899 // do '=' or 'EQU'
900 memmove(curLine, argstart, strlen(argstart)+1);
901 if (!doEQU) {
902 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label %s", lbl->name);
904 int defined = 1;
905 int32_t res = getOneExprArg(&defined);
906 lbl->type = doEQU;
907 if (defined) {
908 lbl->value = res;
909 lbl->known = 1;
910 } else {
911 if (pass != 0) fatal("can't calculate label %s", lbl->name);
913 curLine[0] = '\0';
914 return;
916 // code label
917 if (lbl->name[0] != '{' && !noLocAff) {
918 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
919 lastSeenGlobalLabel = strdup(lbl->name);
921 lbl->type = 2;
922 lbl->value = disp;
923 lbl->known = 1;
927 ///////////////////////////////////////////////////////////////////////////////
928 // instruction finder (in source)
930 /* array ends with NULL */
931 /* returns line or NULL */
932 /* iidx will be set to found instruction number */
933 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
934 if (iidx) *iidx = -1;
935 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
936 va_list ap;
938 //if (!isspace(cur->line[0])) continue; // fuck labeled strings
939 va_start(ap, iidx);
940 for (int 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 optRunTape = 1;
965 static int optRunDMB = 1;
966 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
967 static int optSNA48 = 1;
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++;
987 //printf("%d %u %u\n", cnt+1, byte, addr);
988 if (cnt == 0) {
989 // copy
990 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
991 if (!memused[addr]) putByte(addr, *buf);
992 if (addr == 0xffff) return;
994 } else {
995 // fill
996 for (; cnt > 0; --cnt, ++addr) {
997 if (!memused[addr]) putByte(addr, byte);
998 if (addr == 0xffff) return;
1005 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1006 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1007 if (bxor) *bxor = (*bxor)^b;
1008 return 0;
1012 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1013 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1014 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1015 return 0;
1019 ///////////////////////////////////////////////////////////////////////////////
1020 // .sna
1022 static int saveSna (const char *fname, int as48) {
1023 char *fn = malloc(strlen(fname)+16);
1024 uint8_t regs[27];
1025 FILE *fo;
1026 char abuf[32];
1028 sprintf(fn, "%s.sna", fname);
1029 fo = fopen(fn, "wb");
1030 free(fn);
1031 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1032 printf("out: %s.sna\n", fname);
1033 memmove(regs, ursna48, 27);
1034 #if 0
1035 if (optRunSNA) {
1036 /* push new address */
1037 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1038 sp -= 2;
1039 putByte(sp, ent&0xFFU);
1040 putByte(sp+1, (ent>>8)&0xFFU);
1041 regs[23] = sp&0xFFU;
1042 regs[24] = (sp>>8)&0xFFU;
1044 fwrite(regs, 27, 1, fo);
1045 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1046 fwrite(memory+16384, 49152, 1, fo);
1047 #endif
1049 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1051 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1052 //fprintf(stderr, "%d\n", memused[sp]);
1053 if (memused[sp] || memused[(sp+1)&0xffff]) {
1054 fprintf(stderr, "FATAL: can't save snapshot!\n");
1055 goto error;
1058 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
1059 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
1060 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1062 sprintf(abuf, "%05d", clrAddr);
1063 memcpy(memory+23762, abuf, 5);
1064 sprintf(abuf, "%05d", ent);
1065 memcpy(memory+23773, abuf, 5);
1067 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
1069 if (!as48) {
1070 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
1072 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
1073 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
1074 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
1075 // write pages
1076 memset(memory, 0, 16384);
1077 for (int f = 1; f < 8; ++f) {
1078 if (f != 2 && f != 5) {
1079 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
1084 fclose(fo);
1085 return 0;
1086 error:
1087 fclose(fo);
1088 unlink(fname);
1089 return -1;
1093 ///////////////////////////////////////////////////////////////////////////////
1094 // bin chunks
1096 static void saveRaw (const char *basename) {
1097 char *fname = malloc(strlen(basename)+16);
1098 int start = 0, len;
1099 FILE *fo;
1101 while (findChunkFrom(start, &start, &len)) {
1102 sprintf(fname, "%s_%04X.%s", basename, start, optTapExt?"tap":"bin");
1103 fo = fopen(fname, "wb");
1104 if (!fo) {
1105 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1106 } else {
1107 printf("out: %s\n", fname);
1108 if (fwrite(memory+start, len, 1, fo) != 1) {
1109 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1110 fclose(fo);
1111 unlink(fname);
1112 } else {
1113 fclose(fo);
1116 start += len;
1118 free(fname);
1122 ///////////////////////////////////////////////////////////////////////////////
1123 // .dmb
1125 static int fwriteW16 (FILE *fo, uint16_t w) {
1126 uint8_t b = w&0xff;
1128 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1129 b = (w>>8)&0xff;
1130 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1131 return 0;
1135 static int saveDMB (const char *fname) {
1136 char *fn = malloc(strlen(fname)+16);
1137 int start = 0, len;
1138 uint16_t pcnt = 0;
1139 FILE *fo;
1141 sprintf(fn, "%s.dmb", fname);
1142 fo = fopen(fn, "wb");
1143 free(fn);
1144 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
1146 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
1148 printf("out: %s.dmb\n", fname);
1149 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
1150 if (fwriteW16(fo, (optRunDMB ? ent : 0)) != 0) goto error;
1151 if (fwriteW16(fo, clrAddr) != 0) goto error;
1152 if (fwriteW16(fo, pcnt) != 0) goto error;
1154 start = 0;
1155 while (findChunkFrom(start, &start, &len)) {
1156 if (fwriteW16(fo, len) != 0) goto error;
1157 if (fwriteW16(fo, start) != 0) goto error;
1158 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
1159 start += len;
1161 fclose(fo);
1162 return 0;
1163 error:
1164 fclose(fo);
1165 unlink(fname);
1166 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
1167 return -1;
1171 ///////////////////////////////////////////////////////////////////////////////
1172 // .tap
1174 static char tapeLoaderName[16];
1177 static void saveTapCargador (FILE *fo) {
1178 // count blocks
1179 static uint8_t cargador[16384]; // should be enough for everyone
1180 int start = 0, len, pos, f;
1181 uint8_t bxor;
1184 void putStr (const char *s) {
1185 for (; *s; ++s) cargador[pos++] = *s;
1188 void putNum (int num) {
1189 char buf[64];
1190 sprintf(buf, "%d", num);
1191 putStr(buf);
1195 pos = 4;
1196 // number
1197 cargador[0] = 0; cargador[1] = 10;
1198 // size (will be fixed later)
1199 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
1200 // load blocks
1201 while (findChunkFrom(start, &start, &len)) {
1202 // :LOAD "" CODE
1203 putStr(":\xef\"\"\xaf");
1204 start += len;
1206 // and run
1207 // :RANDOMIZE USR VAL "xxx"
1208 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
1209 // patch len
1210 cargador[2] = (pos-4)&0xff;
1211 cargador[3] = ((pos-4)>>8)&0xff;
1212 // write header
1213 fWriteWord(fo, 19, NULL); // length of header
1214 bxor = 0;
1215 fWriteByte(fo, 0, &bxor); // header block
1216 fWriteByte(fo, 0, &bxor); // 'basic' flag
1217 if (tapeLoaderName[0]) {
1218 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1219 } else {
1220 fWriteByte(fo, 'c', &bxor);
1221 fWriteByte(fo, 'a', &bxor);
1222 fWriteByte(fo, 'r', &bxor);
1223 fWriteByte(fo, 'g', &bxor);
1224 fWriteByte(fo, 'a', &bxor);
1225 fWriteByte(fo, 'd', &bxor);
1226 fWriteByte(fo, 'o', &bxor);
1227 fWriteByte(fo, 'r', &bxor);
1228 fWriteByte(fo, ' ', &bxor);
1229 fWriteByte(fo, ' ', &bxor);
1231 fWriteWord(fo, pos, &bxor); // length
1232 fWriteWord(fo, 10, &bxor); // start
1233 fWriteWord(fo, pos, &bxor); // length2
1234 fWriteByte(fo, bxor, NULL);
1235 // write data
1236 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
1237 bxor = 0;
1238 fWriteByte(fo, 0xFFU, &bxor); // data block
1239 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
1240 fWriteByte(fo, bxor, NULL);
1244 static void saveTap (const char *basename) {
1245 char *fname = malloc(strlen(basename)+16);
1246 char blkname[128];
1247 int start = 0, len, f;
1248 uint8_t bxor;
1249 FILE *fo;
1251 sprintf(fname, "%s.tap", basename);
1252 fo = fopen(fname, "wb");
1253 free(fname);
1254 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
1255 printf("out: %s.tap\n", basename);
1256 if (optRunTape) saveTapCargador(fo);
1257 while (findChunkFrom(start, &start, &len)) {
1258 // write header
1259 if (tapeLoaderName[0]) {
1260 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1261 memcpy(blkname, tapeLoaderName, 10);
1262 } else {
1263 sprintf(blkname, "c%04X:%04X", start, len);
1265 //printf(" block: %s\n", blkname);
1266 fWriteWord(fo, 19, NULL); // length of header
1267 bxor = 0;
1268 fWriteByte(fo, 0, &bxor); // header block
1269 fWriteByte(fo, 3, &bxor); // 'code' flag
1270 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
1271 fWriteWord(fo, len, &bxor);
1272 fWriteWord(fo, start, &bxor);
1273 fWriteWord(fo, 32768, &bxor);
1274 fWriteByte(fo, bxor, NULL);
1275 // write data
1276 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
1277 bxor = 0;
1278 fWriteByte(fo, 0xFFU, &bxor); // data block
1279 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
1280 fWriteByte(fo, bxor, NULL);
1281 start += len;
1283 fclose(fo);
1287 ///////////////////////////////////////////////////////////////////////////////
1288 // pseudoinstructions
1290 // note that processCurrentLine() will NOT skip to the next line before
1291 // calling pseudoinstruction handler!
1292 // note that processCurrentLine() will skip current line after calling
1293 // pseudoinstruction handler!
1295 static int wasOrg = 0;
1296 static int wasClr = 0;
1299 ///////////////////////////////////////////////////////////////////////////////
1300 // user warnings
1302 static int piDISPLAYX (int passNo, int asHex) {
1303 for (;;) {
1304 if (isStrArg()) {
1305 int len = 0;
1306 char *res = getStrArg(&len);
1308 if (passNo < 0 || pass == passNo) printf("%s", res);
1309 } else {
1310 int defined = 1;
1311 int32_t v = getExprArg(&defined);
1313 if (passNo < 0 || pass == passNo) {
1314 if (asHex) printf("%04X", (unsigned int)v);
1315 else printf("%d", v);
1318 if (!skipComma()) break;
1320 return 0;
1324 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
1325 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
1326 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
1327 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
1328 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
1329 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
1332 ///////////////////////////////////////////////////////////////////////////////
1333 // ORG, DISP, etc.
1335 static int piORG (void) {
1336 int defined = 1;
1337 int32_t res = getOneExprArg(&defined);
1339 if (!defined) fatal("sorry, ORG operand value must be known here");
1340 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
1341 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
1342 pc = disp = res;
1343 if (!wasOrg) {
1344 wasOrg = 1;
1345 ent = res;
1346 if (!wasClr && res > 0) clrAddr = res-1;
1348 return 0;
1352 static int piDISP (void) {
1353 int defined = 1;
1354 int32_t res = getOneExprArg(&defined);
1356 if (!defined) fatal("sorry, DISP operand value must be known here");
1357 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
1358 //printf("DISP=%d\n", res);
1359 disp = res;
1360 return 0;
1364 static int piENDDISP (void) {
1365 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1366 checkExprEnd();
1367 disp = pc;
1368 return 0;
1372 static int piENT (void) {
1373 int defined = 1;
1374 int32_t res = getOneExprArg(&defined);
1376 //if (!defined) fatal("sorry, ENT operand value must be known here");
1377 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
1378 ent = res;
1379 return 0;
1383 static int piCLR (void) {
1384 int defined = 1;
1385 int32_t res = getOneExprArg(&defined);
1387 //if (!defined) fatal("sorry, CLR operand value must be known here");
1388 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
1389 clrAddr = res;
1390 wasClr = 1;
1391 return 0;
1395 static int piRESERVE (void) {
1397 int defined = 1, start;
1398 int32_t res = getExprArg(&defined);
1399 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1400 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1401 start = res;
1402 if (!skipComma()) fatal("RESERVE needs 2 args");
1403 res = getOneExprArg(&defined);
1404 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1405 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1406 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
1408 fatal("RESERVE: not yet!");
1409 return 0;
1413 static int piALIGN (void) {
1414 int defined = 1;
1415 int32_t res;
1417 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1418 res = getOneExprArg(&defined);
1419 if (!defined) fatal("sorry, ALIGN operand value must be known here");
1420 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
1421 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
1422 if (res > 0 && pc%res != 0) {
1423 pc /= res;
1424 pc *= res;
1425 pc += res;
1426 disp = pc;
1428 return 0;
1432 static int piDISPALIGN (void) {
1433 int defined = 1;
1434 int32_t res = getOneExprArg(&defined);
1436 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
1437 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
1438 if (res > 0 && disp%res != 0) {
1439 disp /= res;
1440 disp *= res;
1441 disp += res;
1443 return 0;
1447 ///////////////////////////////////////////////////////////////////////////////
1448 // DEFx
1450 static int defIncr = 0;
1453 static int piDEFINCR (void) {
1454 int defined = 1;
1455 int32_t cnt = getOneExprArg(&defined);
1457 if (!defined) fatal("DEFINCR: increment must be defined");
1458 defIncr = cnt;
1459 return 0;
1463 static int piDEFBW (int isWord) {
1464 for (;;) {
1465 int defined = 0;
1467 if (isStrArg()) {
1468 int f, len = 0;
1469 char *res = getStrArg(&len);
1471 for (f = 0; f < len; ++f) {
1472 int32_t b = (uint8_t)res[f];
1473 b += defIncr;
1474 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
1475 if (b < 0) b += 256;
1476 emitByte(b);
1478 } else {
1479 int32_t res = getExprArg(&defined);
1481 if (pass > 0 && !defined) fatal("undefined operand");
1482 res += defIncr;
1483 if (isWord) {
1484 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
1485 if (res < 0) res += 65536;
1486 if (isWord == 1) emitWord(res); else emitRWord(res);
1487 } else {
1488 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
1489 if (res < 0) res += 256;
1490 emitByte(res);
1493 if (!skipComma()) break;
1495 return 0;
1498 static int piDEFB (void) { return piDEFBW(0); }
1499 static int piDEFW (void) { return piDEFBW(1); }
1500 static int piDEFR (void) { return piDEFBW(2); }
1503 static int piDEFS (void) {
1504 for (;;) {
1505 int32_t bt, f;
1506 int defined = 0;
1507 int32_t res = getExprArg(&defined);
1509 if (pass > 0 && !defined) fatal("undefined operand");
1510 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
1511 if (*curLine && curLine[0] == ',') {
1512 skipComma();
1513 bt = getExprArg(&defined);
1514 if (pass > 0 && !defined) fatal("undefined operand");
1515 bt += defIncr;
1516 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
1517 if (bt < 0) bt += 256;
1518 for (f = 0; f < res; ++f) emitByte(bt);
1519 } else {
1520 pc += res; disp += res;
1522 if (!skipComma()) break;
1524 return 0;
1528 /* bit 0: put '\0' */
1529 /* bit 1: set bit 7 of last byte */
1530 /* bit 2: put length */
1531 static int piDEFSTR (int type) {
1532 for (;;) {
1533 if (isStrArg()) {
1534 int f, len = 0;
1535 char *res = getStrArg(&len);
1537 if (type&0x04) {
1538 if (len > 255) fatal("string too long");
1539 emitByte(len);
1541 for (f = 0; f < len; ++f) {
1542 uint8_t b = (uint8_t)res[f];
1544 if ((type&0x02) && f == len-1) b |= 0x80;
1545 emitByte(b);
1547 if (type&0x01) emitByte(0);
1548 } else {
1549 int defined = 1;
1550 int32_t v = getExprArg(&defined);
1552 if (pass > 0 && !defined) fatal("undefined expression");
1553 if (!defined) v = 0; else v += defIncr;
1554 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
1555 if (v < 0) v += 256;
1556 emitByte(v);
1558 if (!skipComma()) break;
1560 return 0;
1564 static int piDEFM (void) { return piDEFSTR(0x00); }
1565 static int piDEFZ (void) { return piDEFSTR(0x01); }
1566 static int piDEFX (void) { return piDEFSTR(0x02); }
1567 static int piDEFC (void) { return piDEFSTR(0x04); }
1570 ///////////////////////////////////////////////////////////////////////////////
1571 // INCBIN
1573 /* INCBIN "name"[,maxlen] */
1574 static int piINCBIN (void) {
1575 int system = 0;
1576 char *fn, qCh;
1577 uint8_t bt;
1578 FILE *fl;
1579 int maxlen = 65536;
1580 char *args = curLine;
1582 if (!curLine[0]) fatal("INCBIN without file name");
1583 if (isStrArg()) {
1584 qCh = *args++;
1585 system = 0;
1586 } else if (curLine[0] == '<') {
1587 qCh = '<'; ++args;
1588 system = 1;
1589 } else {
1590 qCh = 0;
1591 system = 0;
1593 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
1594 if (!fn[0]) fatal("INCBIN: empty file name");
1595 memmove(curLine, args, strlen(args)+1);
1596 // maxlen
1597 if (curLine[0] == ',') {
1598 int defined = 1;
1600 skipComma();
1601 maxlen = getOneExprArg(&defined);
1602 if (!defined) fatal("INCBIN: undefined maxlen");
1603 if (maxlen < 1) return 1; // nothing to do
1605 // now fix name
1606 if (system) {
1607 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
1608 } else {
1609 sprintf(curLine, "%s", fn);
1612 fl = fopen(curLine, "rb");
1613 if (!fl) fatal("INCBIN: file not found: %s", curLine);
1614 while (maxlen-- > 0) {
1615 int res = fread(&bt, 1, 1, fl);
1617 if (!res) break;
1618 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: %s", curLine); }
1619 emitByte(bt);
1621 fclose(fl);
1622 return 1;
1626 ///////////////////////////////////////////////////////////////////////////////
1627 // MODULE, ENDMODULE
1629 static int piENDMODULE (void) {
1630 if (!curModule) fatal("ENDMODULE without MODULE");
1631 if (curLine[0]) {
1632 char *mn = getOneLabelArg();
1634 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
1636 curModule = NULL;
1637 return 0;
1641 static int piMODULE (void) {
1642 ModuleInfo *mi;
1643 char *mn;
1644 SourceLine *ol = curSrcLine;
1645 int inum;
1647 if (curModule) fatal("no nested modules allowed");
1648 mn = getOneLabelArg();
1649 if (!urIsValidLabelName(mn)) fatal("invalid module name: %s", mn);
1650 mi = moduleFind(mn);
1651 //printf("[%s] %p\n", mn, mi);
1652 if (mi) {
1653 if (mi->seen) {
1654 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
1655 /* skip module */
1656 nextSrcLine(); // skip ourself
1657 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
1658 setCurSrcLine(ol);
1659 fatal("no ENDMODULE");
1661 if (inum == 0) fatal("no nested modules allowed");
1662 curModule = mi;
1663 skipInstruction();
1664 piENDMODULE();
1665 return 0;
1667 } else {
1668 mi = moduleAdd(mn, curSrcLine->fname);
1670 mi->seen = 1;
1671 curModule = mi;
1672 return 0;
1676 ///////////////////////////////////////////////////////////////////////////////
1677 // DUP, EDUP
1679 static int piEDUP (void) {
1680 fatal("EDUP without DUP");
1681 checkExprEnd();
1682 return 1;
1686 static int piDUP (void) {
1687 int defined = 1, dupCnt = 1, inum;
1688 SourceLine *stline, *eline = NULL;
1689 int32_t cnt = getOneExprArg(&defined);
1691 if (!defined) fatal("DUP: counter must be defined");
1692 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
1693 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
1694 // now find corresponding EDUP
1695 // note that we should skip nested DUPs
1696 nextSrcLine(); // skip ourself
1697 stline = curSrcLine;
1698 while (curSrcLine) {
1699 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
1700 // ok, we found something; what is it?
1701 if (inum == 0) {
1702 // new DUP, skip it
1703 ++dupCnt;
1704 nextSrcLine(); // skip DUP
1705 } else {
1706 // EDUP
1707 if (--dupCnt == 0) {
1708 // gotcha!
1709 eline = curSrcLine;
1710 break;
1712 nextSrcLine(); // skip EDUP
1715 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
1716 // now repeat that lines
1717 while (cnt-- > 0) {
1718 setCurSrcLine(stline);
1719 while (curSrcLine != eline) processCurrentLine();
1721 return 1;
1725 ///////////////////////////////////////////////////////////////////////////////
1726 // IF, ENDIF
1728 static int ifCount = 0;
1731 static int ifSkipToEndIfOrElse (int wholeBody) {
1732 int inum, wasElse = 0;
1733 SourceLine *oline;
1735 while (curSrcLine) {
1736 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
1737 switch (inum) {
1738 case 0: /* if */
1739 case 6: /* ifx */
1740 nextSrcLine(); // skip IF
1741 ifSkipToEndIfOrElse(1); // and recurse
1742 nextSrcLine(); // skip ENDIF
1743 break;
1744 case 1: /* else */
1745 if (wasElse) fatal("duplicate ELSE");
1746 if (!wholeBody) return 1;
1747 wasElse = 1;
1748 nextSrcLine(); // skip ELSE
1749 break; // and continue
1750 case 2: /* endif */
1751 return 0; // and exit
1752 case 3: /* elif */
1753 case 7: /* elifx */
1754 if (wasElse) fatal("ELSEIF in ELSE");
1755 if (!wholeBody) return 2;
1756 nextSrcLine(); // skip ELSEIF
1757 break; // and continue
1758 case 4: /* macro */
1759 // skip it as a whole
1760 nextSrcLine(); // skip MACRO
1761 for (;;) {
1762 oline = curSrcLine;
1763 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
1764 if (inum == 1) break;
1765 fatal("invalid nested MACRO");
1767 nextSrcLine(); // skip ENDM
1768 break;
1769 case 5: /* endm */
1770 fatal("unexpected ENDM");
1773 fatal("IF without ENDIF");
1774 return -1;
1778 static int piENDIF (void) {
1779 if (--ifCount < 0) fatal("ENDIF without IF");
1780 checkExprEnd();
1781 return 1;
1785 static int piELSE (void) {
1786 if (--ifCount < 0) fatal("ELSE without IF");
1787 nextSrcLine(); // skip ELSE
1788 ifSkipToEndIfOrElse(1);
1789 return 1;
1793 static int piELSEIF (void) {
1794 if (--ifCount < 0) fatal("ELSEIF without IF");
1795 nextSrcLine(); // skip ELSEIF
1796 ifSkipToEndIfOrElse(1);
1797 return 1;
1801 static int piIFAll (int isIfX) {
1802 int defined = 1;
1803 int ooo = lblOptMakeU2;
1804 lblOptMakeU2 = isIfX ? 1 : 0;
1805 int32_t cond = getOneExprArg(&defined);
1806 lblOptMakeU2 = ooo;
1808 if (!defined) {
1809 if (!isIfX) fatal("IF: condition must be defined");
1810 cond = 0; // for IFX: 0 is there is any undefined label
1812 if (cond) {
1813 // ok, do it until ELSE/ELSEIF/ENDIF
1814 ++ifCount;
1815 return 1;
1817 for (;;) {
1818 int r;
1819 char *args;
1821 nextSrcLine(); // skip last instruction
1822 // skip until ELSE/ELSEIF/ENDIF
1823 r = ifSkipToEndIfOrElse(0);
1824 if (r == 0) break; // ENDIF
1825 if (r == 1) { ++ifCount; break; } // ELSE
1826 // ELSEIF, do condition
1827 if ((args = strIsCommand("ELSEIF", curLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
1828 memmove(curLine, args, strlen(args)+1);
1829 cond = getOneExprArg(&defined);
1830 if (!defined) fatal("ELSEIF: condition must be defined");
1831 if (cond) { ++ifCount; break; } // condition is true
1833 return 1;
1837 static int piIF (void) { return piIFAll(0); }
1838 static int piIFX (void) { return piIFAll(1); }
1841 ///////////////////////////////////////////////////////////////////////////////
1842 // macro processor
1844 * what i did with MACRO is the brain-damaged cheating all the way.
1845 * first, i will collect the MACRO body and remember it.
1846 * second, when the macro is used, i will:
1847 * * create unique labels for all supplied macro args, each with
1848 * number (so IFARG/IFNARG can check the existance)
1849 * * insert the whole macro body in place, fixing argument refs
1850 * * let the asm play with it
1851 * another tricky part is 'local labels': i have to change all
1852 * '..lbl' references -- generate new label name for it and
1853 * replace all refs. and be careful to not touch the strings.
1854 * this is not the best scheme, but it is fairly simple and it wokrs.
1856 static int piMACRO (void) {
1857 fatal("sorry, no MACRO yet");
1858 return 1;
1862 static int piENDM (void) {
1863 fatal("ENDM withoud MACRO");
1864 return 1;
1868 ///////////////////////////////////////////////////////////////////////////////
1869 // line processor
1870 static int optWriteType = 't';
1871 static int optWTChanged = 0;
1874 static void piTapParseLoaderName (void) {
1875 if (skipComma()) {
1876 int len;
1877 char *fn;
1879 if (!isStrArg()) fatal("loader name expected");
1880 fn = getStrArg(&len);
1881 if (len > 10) fatal("loader name too long");
1882 memset(tapeLoaderName, ' ', 10);
1883 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
1888 static int piDEFFMT (void) {
1889 char *name;
1891 if (isStrArg()) {
1892 int len = 0;
1894 name = getStrArg(&len);
1895 } else {
1896 name = getLabelArg();
1898 if (optWTChanged) return 1;
1899 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA")) {
1900 optRunTape = 0;
1901 //optRunSNA = (toupper(name[0]) == 'R');
1902 optWriteType = 's';
1903 if (curLine[0]) fatal("too many expressions");
1904 return 1;
1906 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
1907 optRunTape = 0;
1908 //optRunSNA = 0;
1909 optWriteType = 't';
1910 piTapParseLoaderName();
1911 return 1;
1913 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE")) {
1914 optRunTape = 1;
1915 //optRunSNA = 0;
1916 optWriteType = 't';
1917 piTapParseLoaderName();
1918 return 1;
1920 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
1921 optRunTape = 0;
1922 //optRunSNA = 0;
1923 optWriteType = 'r';
1924 if (curLine[0]) fatal("too many expressions");
1925 return 1;
1927 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB")) {
1928 optRunTape = 0;
1929 //optRunSNA = 0;
1930 optRunDMB = (toupper(name[0]) == 'R');
1931 optWriteType = 'd';
1932 if (curLine[0]) fatal("too many expressions");
1933 return 1;
1935 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
1936 optRunTape = 0;
1937 //optRunSNA = 0;
1938 optWriteType = 'n';
1939 if (curLine[0]) fatal("too many expressions");
1940 return 1;
1942 fatal("invalid default output type: %s", name);
1946 ///////////////////////////////////////////////////////////////////////////////
1947 // line processor
1949 static void processCurrentLine (void) {
1950 if (!curSrcLine) return; // do nothing
1951 loadCurSrcLine();
1952 processLabel();
1953 for (;;) {
1954 char *str, *ee, name[66];
1955 UrAsmOp *op;
1956 int len;
1957 const char *errpos;
1959 removeSpacesAndColons();
1960 // skip spaces and ':'
1961 if (!curLine[0]) { nextSrcLine(); break; }
1962 // try to find and process command
1963 str = curLine; //while (*str && isspace(*str)) ++str; // skip spaces
1964 // find command end
1965 for (ee = str; *ee && !isspace(*ee) && *ee != '"' && *ee != '\''; ++ee) ;
1966 // get command, if any
1967 if (ee != str && ee-str <= 64) {
1968 memset(name, 0, sizeof(name));
1969 memmove(name, str, ee-str);
1970 // known command?
1971 op = urFindOp(name);
1972 if (op) {
1973 // ok, do it
1974 str = ee; while (*str && isspace(*str)) ++str; // skip spaces
1975 memmove(curLine, str, strlen(str)+1);
1976 if (op->fn()) {
1977 nextSrcLine(); // skip it
1978 break;
1983 len = urAssembleOne(curLine, pc, disp, &errpos);
1984 if (len < 0) fatalUrLib(len);
1985 pc += len; disp += len;
1986 if (len >= 0 && errpos) {
1987 memmove(curLine, errpos+1, strlen(errpos));
1988 } else {
1989 nextSrcLine(); // skip it
1990 break;
1996 ///////////////////////////////////////////////////////////////////////////////
1997 // setup instructions
1999 static void registerInstructions (void) {
2000 urAddOp("DISPLAY", piDISPLAY);
2001 urAddOp("DISPLAY0", piDISPLAY0);
2002 urAddOp("DISPLAYA", piDISPLAYA);
2003 urAddOp("DISPHEX", piDISPHEX);
2004 urAddOp("DISPHEX0", piDISPHEX0);
2005 urAddOp("DISPHEXA", piDISPHEXA);
2007 urAddOp("DEFFMT", piDEFFMT);
2009 urAddOp("MACRO", piMACRO);
2010 urAddOp("ENDM", piENDM);
2012 urAddOp("ORG", piORG);
2013 urAddOp("DISP", piDISP);
2014 urAddOp("ENDDISP", piENDDISP);
2015 urAddOp("PHASE", piDISP);
2016 urAddOp("DEPHASE", piENDDISP);
2017 urAddOp("UNPHASE", piENDDISP);
2018 urAddOp("ALIGN", piALIGN);
2019 urAddOp("DISPALIGN", piDISPALIGN);
2020 urAddOp("PHASEALIGN", piDISPALIGN);
2021 urAddOp("ENT", piENT);
2022 urAddOp("CLR", piCLR);
2023 urAddOp("RESERVE", piRESERVE);
2025 urAddOp("INCBIN", piINCBIN);
2027 urAddOp("MODULE", piMODULE);
2028 urAddOp("ENDMODULE", piENDMODULE);
2030 urAddOp("DUP", piDUP);
2031 urAddOp("EDUP", piEDUP);
2033 urAddOp("IF", piIF);
2034 urAddOp("IFX", piIFX);
2035 urAddOp("ELSE", piELSE);
2036 urAddOp("ELSEIF", piELSEIF);
2037 urAddOp("ELSEIFX", piELSEIF);
2038 urAddOp("ENDIF", piENDIF);
2040 urAddOp("DEFINCR", piDEFINCR);
2041 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
2042 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
2043 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
2044 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
2045 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
2046 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
2047 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
2048 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
2052 ///////////////////////////////////////////////////////////////////////////////
2053 // !0: invalid label
2055 static inline void fnSkipSpaces (const char *expr) {
2056 while (*expr && isspace(*expr)) ++expr;
2057 return expr;
2062 static inline char fnNextChar (const char *expr) {
2063 while (*expr && isspace(*expr)) ++expr;
2064 return *expr;
2068 #define FN_CHECK_END do { \
2069 while (*expr && isspace(*expr)) ++expr; \
2070 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
2071 ++expr; \
2072 } while (0)
2075 #define FN_CHECK_COMMA do { \
2076 while (*expr && isspace(*expr)) ++expr; \
2077 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
2078 ++expr; \
2079 } while (0)
2082 static const char *readLabelName (char *buf, const char *expr) {
2083 int pos = 0;
2085 while (*expr && isspace(*expr)) ++expr;
2086 for (;;) {
2087 char ch = *expr++;
2089 if (pos >= 128) return NULL;
2090 if (!ch) break;
2091 if (isalnum(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
2092 buf[pos++] = ch;
2093 } else {
2094 break;
2097 if (pos < 1) return NULL;
2098 buf[pos] = '\0';
2099 if (!urIsValidLabelName(buf)) return NULL;
2100 return expr;
2104 static const char *fnDefKn (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
2105 char lbl[130];
2107 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
2108 FN_CHECK_END;
2109 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
2110 return expr;
2113 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); }
2114 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); }
2117 static const char *fnAligned256 (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2118 expr = urExpressionEx(res, expr, addr, &donteval, defined, error);
2119 FN_CHECK_END;
2120 if (!donteval) res->val = (res->val%256 ? 0 : 1);
2121 return expr;
2125 static const char *fnSameSeg (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2126 URExprValue v0, v1;
2128 urInitExprValue(&v0);
2129 urInitExprValue(&v1);
2130 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2131 FN_CHECK_COMMA;
2132 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2133 FN_CHECK_END;
2134 if (!donteval) res->val = (v0.val/256 == v1.val/256);
2135 return expr;
2139 static const char *fnAlign (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2140 URExprValue v0, v1;
2142 urInitExprValue(&v0);
2143 urInitExprValue(&v1);
2144 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2145 if (fnNextChar(expr) == ',') {
2146 FN_CHECK_COMMA;
2147 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2148 } else {
2149 v1.val = 256;
2151 FN_CHECK_END;
2152 if (!donteval) {
2153 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
2154 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
2156 return expr;
2160 static void registerFunctions (void) {
2161 urExpressionRegisterFunction("defined", fnDefined);
2162 urExpressionRegisterFunction("known", fnKnown);
2163 urExpressionRegisterFunction("aligned256", fnAligned256);
2164 urExpressionRegisterFunction("align", fnAlign);
2165 urExpressionRegisterFunction("sameseg", fnSameSeg);
2169 ///////////////////////////////////////////////////////////////////////////////
2170 // preparing another pass
2172 static void initPass (void) {
2173 curSrcLine = asmText;
2174 curModule = NULL;
2175 pc = disp = ent = 0x100; // viva CP/M!
2176 inTapeBlock = 0;
2177 tapeXorB = 0;
2178 wasOrg = 0;
2179 wasClr = 0;
2180 ifCount = 0;
2181 defIncr = 0;
2182 lblOptMakeU2 = 0;
2183 lastSeenGlobalLabel = strdup(" [MAIN] ");
2184 modulesResetSeen();
2185 prepareMemory();
2189 static int posstPass (void) {
2190 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
2191 if (checkLabels()) return -1;
2192 if (ifCount != 0) fatal("unbalanced IFs");
2193 return 0;
2197 ///////////////////////////////////////////////////////////////////////////////
2198 // options
2200 static struct option longOpts[] = {
2201 {"sna", 0, NULL, 's'},
2202 {"sna128", 0, NULL, 'S'},
2203 {"tap", 0, NULL, 't'},
2204 {"autotap", 0, NULL, 'T'},
2205 /*{"rawtap", 0, NULL, 'T'},*/
2206 {"raw", 0, NULL, 'r'},
2207 {"autodmb", 0, NULL, 'B'},
2208 {"dmb", 0, NULL, 'b'},
2209 {"none", 0, NULL, 'n'},
2210 {"help", 0, NULL, 'h'},
2211 {NULL, 0, NULL, 0}
2215 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
2218 static void usage (const char *pname) {
2219 printf(
2220 "usage: %s [options] infile\n"
2221 "default infiles:", pname);
2222 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
2223 printf("\n"
2224 "options:\n"
2225 " -s --sna write 48K .SNA file with autostart\n"
2226 " -S --sna128 write 148K .SNA file with autostart\n"
2227 " -t --tap write .tap file\n"
2228 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
2229 " -r --raw write raw file(s)\n"
2230 " -b --dmb write DMB file\n"
2231 " -B --autodmb write DMB file with autostart\n"
2232 " -n --none write nothing\n"
2233 " -h --help this help\n");
2237 ///////////////////////////////////////////////////////////////////////////////
2238 // main
2240 int main (int argc, char *argv[]) {
2241 int res = 0, c;
2242 const char *pname = argv[0];
2243 char *inFile = NULL;
2244 initInclideDir();
2246 urGetByteFn = getByte;
2247 urPutByteFn = putByte;
2248 urFindLabelByNameFn = findLabelCB;
2249 //urIsLabelDefinedOrKnownFn = isLabelDefinedOrKnown;
2251 //strcpy(tapeLoaderName, "cargador ");
2252 tapeLoaderName[0] = 0;
2254 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
2255 while ((c = getopt_long(argc, argv, "sStTbBrnh", longOpts, NULL)) >= 0) {
2256 switch (c) {
2257 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
2258 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
2259 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
2260 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
2261 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
2262 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
2263 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
2264 case 'h': usage(pname); res = 0; goto earlyerrquit;
2265 case '?': return 1;
2268 if (optind >= argc) {
2269 // try to find default input file
2270 int f;
2271 for (f = 0; defInFiles[f]; ++f) {
2272 if (!access(defInFiles[f], R_OK)) {
2273 inFile = strdup(defInFiles[f]);
2274 break;
2277 } else {
2278 inFile = strdup(argv[optind]);
2280 if (!inFile || !inFile[0]) {
2281 res = 1;
2282 fprintf(stderr, "ERROR: no input file!\n");
2283 goto earlyerrquit;
2286 registerInstructions();
2287 registerFunctions();
2289 res = asmTextLoad(inFile);
2290 if (!res) {
2292 printf("dumping...\n");
2293 FILE *fo = fopen("z000.out", "w");
2294 if (fo) {
2295 SourceLine *c;
2296 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
2297 fclose(fo);
2300 for (pass = 0; pass <= 1; ++pass) {
2301 initPass();
2302 printf("pass %d\n", pass);
2303 setCurSrcLine(asmText);
2304 if (setjmp(errJP)) { res = 1; break; }
2305 while (curSrcLine) processCurrentLine();
2306 if (posstPass()) { res = 1; break; }
2308 // write result
2309 if (res == 0) {
2310 char *oc = strdup(inFile);
2311 char *pd = strrchr(oc, '.');
2312 if (pd && !strchr(oc, '/')) *pd = '\0';
2313 switch (optWriteType) {
2314 case 's': saveSna(oc, optSNA48); break;
2315 case 't': saveTap(oc); break;
2316 case 'r': saveRaw(oc); break;
2317 case 'd': saveDMB(oc); break;
2319 free(oc);
2321 } else {
2322 fprintf(stderr, "ERROR: loading error!\n");
2325 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
2326 urClearLabels();
2327 modulesClear();
2328 asmTextClear();
2329 urClearOps();
2330 earlyerrquit:
2331 if (inFile) free(inFile);
2332 if (sysIncludeDir) free(sysIncludeDir);
2333 return res?1:0;