urasm: fixup table writer
[urasm.git] / src / urasm.c
blobcc5a3a6dc4c7d30224dd577bc1ea5de7d44343e6
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 {
217 struct SourceLine *next;
218 char *line;
219 char *fname;
220 int lineNo;
221 } SourceLine;
223 static SourceLine *asmText = NULL;
224 static SourceLine *asmTextLast = NULL;
225 static SourceLine *curSrcLine = NULL;
228 typedef struct MacroDef {
229 struct MacroDef *next;
230 char *name;
231 SourceLine *lines;
232 int argc;
233 char *argdef[32]; // default values
234 char *argnames[32]; // argument names
235 } MacroDef;
237 typedef struct {
238 MacroDef *mac;
239 char *argvals[32]; // argument values
240 } CurMacroDef;
242 static MacroDef *maclist = NULL;
243 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
244 static int curmacronum;
247 static void asmTextClear (void) {
248 while (asmText) {
249 SourceLine *l = asmText;
251 asmText = asmText->next;
252 free(l->line);
253 free(l->fname);
254 free(l);
256 asmTextLast = curSrcLine = NULL;
260 static void normIncName (char *dest, const char *fn, int system) {
261 struct stat st;
263 if (system) sprintf(dest, "%s/%s", sysIncludeDir, fn); else sprintf(dest, "%s", fn);
264 if (stat(dest, &st)) return;
265 if (S_ISDIR(st.st_mode)) strcat(dest, "/zzmain.zas");
269 static int asmTextLoad (const char *fname) {
270 FILE *fl;
271 int lineNo = 0;
272 char *args;
273 SourceLine *s;
274 static int includeCount = 0;
276 if (!(fl = fopen(fname, "r"))) {
277 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
278 return -1;
280 printf("loading: %s\n", fname);
281 // read file
282 while (fgets(curLine, sizeof(curLine)-1, fl)) {
283 ++lineNo;
284 curLine[sizeof(curLine)-1] = '\0';
285 normalizeStr(curLine);
286 //fprintf(stderr, "*[%s]\n", curLine);
287 if (!curLine[0]) continue; // don't store empty lines
288 // find specials, if any
289 if (isspace(curLine[0])) {
290 if ((args = strIsCommand("INCLUDE", curLine)) != NULL) {
291 // process 'INCLUDE'
292 int system = 0;
293 char qCh = 0, *fn;
295 if (!args[0]) {
296 fclose(fl);
297 fprintf(stderr, "ERROR: file %s, line %d: invalid INCLUDE!\n", fname, lineNo);
298 return -1;
300 if (args[0] == '"' || args[0] == '\'') qCh = *args++;
301 else if (args[0] == '<') { qCh = '>'; ++args; system = 1; }
303 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
304 if (!fn[0]) {
305 fclose(fl);
306 fprintf(stderr, "ERROR: file %s, line %d: can't INCLUDE nothing!\n", fname, lineNo);
307 return -1;
309 if (args[0]) {
310 fclose(fl);
311 fprintf(stderr, "ERROR: file %s, line %d: extra args after INCLUDE filename!\n", fname, lineNo);
312 return -1;
314 if (includeCount > 256) {
315 fclose(fl);
316 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", fname, lineNo);
317 return -1;
319 normIncName(curLine, fn, system);
320 //if (system) sprintf(curLine, "%s/%s", sysIncludeDir, fn); else sprintf(curLine, "%s", fn);
321 if ((fn = strdup(curLine)) == NULL) abort();
322 ++includeCount;
323 system = asmTextLoad(fn);
324 --includeCount;
325 free(fn);
326 if (system) { fclose(fl); return system; }
327 continue;
330 // add current line
331 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
332 s->lineNo = lineNo;
333 if ((s->line = strdup(curLine)) == NULL) abort();
334 if ((s->fname = strdup(fname)) == NULL) abort();
335 if (asmTextLast) asmTextLast->next = s; else asmText = s;
336 asmTextLast = s;
338 fclose(fl);
339 return 0;
343 static inline void loadCurSrcLine (void) { if (curSrcLine) strcpy(curLine, (curSrcLine != NULL ? curSrcLine->line : "")); }
344 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
345 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
348 ///////////////////////////////////////////////////////////////////////////////
349 // prototypes
351 static void processCurrentLine (void); // only one, will skip to next one
354 ///////////////////////////////////////////////////////////////////////////////
355 // error raisers, etc
357 static jmp_buf errJP;
360 static void errorWriteFile (void) {
361 if (curSrcLine) {
362 fprintf(stderr, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
363 } else {
364 fprintf(stderr, "somewhere in time: ");
368 static void errorMsgV (const char *fmt, va_list ap) {
369 errorWriteFile();
370 vfprintf(stderr, fmt, ap);
371 va_end(ap);
372 fputc('\n', stderr);
373 fflush(stderr);
377 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
378 va_list ap;
379 fprintf(stderr, "WARNING ");
380 va_start(ap, fmt);
381 errorMsgV(fmt, ap);
385 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
386 va_list ap;
387 fprintf(stderr, "FATAL ");
388 va_start(ap, fmt);
389 errorMsgV(fmt, ap);
393 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
394 va_list ap;
395 va_start(ap, fmt);
396 errorMsgV(fmt, ap);
397 longjmp(errJP, 666);
401 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
402 errorMsg("%s", urErrorMessage(errcode));
403 longjmp(errJP, 666);
407 //////////////////////////////////////////////////////////////////////////////
408 // operator management
410 typedef int (*UrAsmOpFn) (void);
412 typedef struct UrAsmOp {
413 char *name;
414 UrAsmOpFn fn;
415 struct UrAsmOp *next;
416 } UrAsmOp;
418 static UrAsmOp *oplist = NULL;
421 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
422 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
423 if (!res) abort();
424 res->name = strdup(name);
425 res->fn = fn;
426 res->next = oplist;
427 oplist = res;
428 return res;
432 static UrAsmOp *urFindOp (const char *name) {
433 UrAsmOp *res;
434 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
435 return res;
439 static void urClearOps (void) {
440 while (oplist) {
441 UrAsmOp *c = oplist;
443 oplist = oplist->next;
444 free(c->name);
445 free(c);
450 ///////////////////////////////////////////////////////////////////////////////
451 // label management
453 typedef struct UrLabelInfo {
454 char *name;
455 int32_t value;
456 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
457 int known; /* !0: label value already known */
458 int refLine; /* first referenced line */
459 int fixuptype; /* UR_FIXUP_XXX */
460 char *refFile;
461 struct UrLabelInfo *next;
462 } UrLabelInfo;
464 static UrLabelInfo *labels = NULL;
467 static void urClearLabels (void) {
468 UrLabelInfo *c;
470 while ((c = labels) != NULL) {
471 labels = c->next;
472 if (c->name) free(c->name);
473 if (c->refFile) free(c->refFile);
474 free(c);
479 static UrLabelInfo *urFindLabel (const char *name) {
480 for (UrLabelInfo *c = labels; c; c = c->next) if (strcmp(name, c->name) == 0) return c;
481 return NULL;
485 static UrLabelInfo *urAddLabel (const char *name) {
486 UrLabelInfo *c = urFindLabel(name);
488 if (c == NULL) {
489 UrLabelInfo *p;
491 for (p = NULL, c = labels; c; p = c, c = c->next) ;
492 c = calloc(1, sizeof(UrLabelInfo));
493 if (!c) abort();
494 c->name = strdup(name);
495 c->type = -1;
496 c->fixuptype = UR_FIXUP_NONE;
497 if (p) p->next = c; else labels = c;
498 c->next = NULL;
500 return c;
504 ///////////////////////////////////////////////////////////////////////////////
505 // module list management
507 typedef struct ModuleInfo {
508 char *name;
509 char *fname; // opened in this file
510 int seen; // !0: module already seen, skip other definitions from the same file
511 struct ModuleInfo *next;
512 } ModuleInfo;
514 static ModuleInfo *modules = NULL;
515 static ModuleInfo *curModule = NULL;
518 static void modulesClear (void) {
519 curModule = NULL;
520 while (modules) {
521 ModuleInfo *c = modules;
523 modules = modules->next;
524 free(c->name);
525 free(c->fname);
526 free(c);
531 static void modulesResetSeen (void) {
532 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
536 static ModuleInfo *moduleFind (const char *name) {
537 if (!name || !name[0]) return NULL;
538 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
539 return NULL;
543 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
544 ModuleInfo *c;
546 if (!name || !fname || !name[0] || !fname[0]) abort();
547 if ((c = calloc(1, sizeof(ModuleInfo))) == NULL) abort();
548 if ((c->name = strdup(name)) == NULL) abort();
549 if ((c->fname = strdup(fname)) == NULL) abort();
550 c->next = modules;
551 return (modules = c);
555 ///////////////////////////////////////////////////////////////////////////////
556 // fixup management
557 typedef struct FixupItem {
558 struct FixupItem *next;
559 uint16_t opdestaddr;
560 uint16_t opaddr;
561 int fixuptype;
562 int size;
563 } FixupItem;
564 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
567 static void clearFixups (void) {
568 FixupItem *c;
570 while ((c = fixlisthead) != NULL) {
571 fixlisthead = c->next;
572 free(c);
577 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
578 FixupItem *fx = calloc(1, sizeof(FixupItem));
580 fx->opdestaddr = opdestaddr;
581 fx->opaddr = opaddr;
582 fx->fixuptype = fixuptype;
583 fx->size = size;
585 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
586 fx->next = NULL;
587 fixlisttail = fx;
591 ///////////////////////////////////////////////////////////////////////////////
592 // destination memory management
594 static uint8_t memory[65536];
595 static char memused[65536];
596 static char memresv[65536];
597 static uint16_t pc = 0; /* current position to write */
598 static uint16_t disp = 0; /* current 'virtual PC' */
599 static uint16_t ent = 0; /* starting address */
600 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
601 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
602 static int inTapeBlock = 0;
603 static uint8_t tapeXorB = 0;
606 static inline uint8_t getByte (uint16_t addr) {
607 return memory[addr];
612 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
613 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
618 static inline void putByte (uint16_t addr, uint8_t b) {
619 if (inTapeBlock) tapeXorB ^= b;
620 memory[addr] = b;
621 memused[addr] = 1;
625 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
626 putByte(addr, w&0xFFU);
627 putByte(addr+1, (w>>8)&0xFFU);
631 static inline void emitByte (uint8_t b) {
632 putByte(pc, b);
633 ++pc;
634 ++disp;
638 static inline void emitWord (uint16_t w) {
639 emitByte(w&0xFFU);
640 emitByte((w>>8)&0xFFU);
644 static inline void emitRWord (uint16_t w) {
645 emitByte((w>>8)&0xFFU);
646 emitByte(w&0xFFU);
650 static void prepareMemory (void) {
651 memset(memory, 0, sizeof(memory));
652 memset(memused, 0, sizeof(memused));
653 memset(memresv, 0, sizeof(memresv));
657 ///////////////////////////////////////////////////////////////////////////////
658 // label getter and utilities
660 static char *lastSeenGlobalLabel = NULL; /* global */
663 static char *fixLocalLabel (const char *name) {
664 static char newname[MAX_LINE_SIZE*2+1024];
666 memset(newname, 0, sizeof(newname));
667 if (!name || !name[0]) {
668 newname[0] = '\0';
669 } else if (!lastSeenGlobalLabel || name[0] != '.') {
670 strcpy(newname, name);
671 } else {
672 if (name[0] == '.' && name[1] == '.') {
673 // this is macro label
674 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
675 sprintf(newname, "{#MAC%d:%s:%s}", curmacronum, curmacro->mac->name, name);
676 } else {
677 // this is local label, let's rename it
678 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
681 return newname;
685 static char *fixGlobalLabel (const char *name) {
686 static char newname[MAX_LINE_SIZE*2+1024];
688 memset(newname, 0, sizeof(newname));
689 if (!name || !name[0]) {
690 newname[0] = '\0';
691 } else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
692 if (name[0] == '@' && name[1]) ++name;
693 strcpy(newname, name);
694 } else {
695 // this is global unqualified label and we have a module; let's rename it
696 sprintf(newname, "%s.%s", curModule->name, name);
698 //printf("%s --> %s\n", name, newname);
699 return newname;
703 static int lblOptMakeU2 = 0;
705 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
706 UrLabelInfo *lbl;
707 char *ln, *nn;
709 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
710 lbl = urFindLabel(nn);
711 if (!lbl) {
712 // try non-module label
713 lbl = urFindLabel(ln);
715 if (!lbl) {
716 if (pass != 0) {
717 errorMsg("using undefined label %s", ln);
718 *found = 0;
719 *defined = 0;
720 return 0;
722 lbl = urAddLabel(nn);
723 lbl->type = (lblOptMakeU2 ? -42 : -1);
724 lbl->known = 0;
725 lbl->refLine = curSrcLine->lineNo;
726 lbl->refFile = strdup(curSrcLine->fname);
727 //printf("new label: [%s]\n", lbl->name);
728 } else {
729 //printf("label reference: [%s]\n", lbl->name);
731 if (lbl) {
732 *found = 1;
733 *defined = lbl->known!=0;
734 *fixuptype = lbl->fixuptype;
735 return lbl->value;
737 *found = 0;
738 *defined = 0;
739 return 0;
743 // qtypes
744 enum {
745 UR_QTYPE_DEFINED,
746 UR_QTYPE_KNOWN
749 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
750 UrLabelInfo *lbl;
751 char *ln, *nn;
753 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
754 lbl = urFindLabel(nn);
755 if (!lbl) {
756 // try non-module label
757 lbl = urFindLabel(ln);
759 switch (qtype) {
760 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
761 case UR_QTYPE_KNOWN: return lbl!=NULL;
762 default: ;
764 return 0;
768 static void fixupOperandCB (const URAOperand *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
769 if (pass == 1) {
770 //static const char *n[4] = {"none", "word", "low", "high"};
771 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
772 addFixup(opdestaddr, opaddr, fixuptype, size);
777 static int checkLabels (void) {
778 int wasError = 0;
780 for (UrLabelInfo *c = labels; c; c = c->next) {
781 if (c->type == -1) {
782 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
783 wasError = 1;
785 if (c->type == 0) c->known = -1;
787 //if (wasError) longjmp(errJP, 667);
788 return wasError;
792 ///////////////////////////////////////////////////////////////////////////////
793 // expression utils
795 static inline char *strSkipSpaces (const char *s) {
796 while (*s && isspace(*s)) ++s;
797 return (char *)s;
801 static inline char *strSkipSpacesColons (char *s) {
802 while (*s && (isspace(*s) || *s == ':')) ++s;
803 return s;
807 static inline void skipSpaces (void) {
808 char *e = strSkipSpaces(curLine);
810 if (e != curLine) memmove(curLine, e, strlen(e)+1);
814 static char *skipArg (char *str) {
815 for (;;) {
816 str = strSkipSpaces(str);
817 if (!str[0] || str[0] == ',' || str[0] == ':') return str;
818 if (str[0] == '"' || str[0] == '\'') {
819 char qch = *str++;
821 while (*str && *str != qch) {
822 if (*str++ == '\\') {
823 switch (*str++) {
824 case 'x': case 'X':
825 for (int f = 0; f < 2; ++f, ++str) if (!isxdigit(*str)) break;
826 break;
827 case '0':
828 for (int f = 0; f < 4; ++f, ++str) if (*str < '0' || *str > '7') break;
829 break;
830 case '1' ... '9':
831 for (int f = 0; f < 3; ++f, ++str) if (!isdigit(*str)) break;
832 break;
836 continue;
838 ++str;
843 static int32_t getExprArg (int *defined, int *addr) {
844 int error = 0;
845 char *a = strSkipSpaces(curLine);
846 const char *ee;
847 int32_t res;
849 if (!a[0]) fatal("expression expected");
850 ee = urExpression(&res, a, disp, defined, addr, &error);
851 if (error) fatalUrLib(error);
852 if (*ee) {
853 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
854 memmove(curLine, ee, strlen(ee)+1);
855 } else {
856 curLine[0] = '\0';
858 return res;
862 static int32_t getOneExprArg (int *defined, int *addr) {
863 int32_t res = getExprArg(defined, addr);
865 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
866 return res;
870 static inline int isStrArg (void) { return (curLine[0] == '"' || curLine[0] == '\''); }
873 static char *getStrArg (int *lenp) {
874 char *res, qCh;
875 char *a = strSkipSpaces(curLine);
877 qCh = *a++;
878 if (qCh != '"' && qCh != '\'') fatal("string expected");
879 res = parseStr(&a, qCh, lenp);
880 if (*a) {
881 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
882 memmove(curLine, a, strlen(a)+1);
883 } else {
884 curLine[0] = '\0';
886 return res;
890 static MAYBE_UNUSED char *getOneStrArg (int *lenp) {
891 char *res = getStrArg(lenp);
893 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
894 return res;
898 static char *getLabelArg (int checkdelim) {
899 static char res[MAX_LINE_SIZE+128], *p;
900 char *a = strSkipSpaces(curLine);
902 memset(res, 0, sizeof(res));
903 if (!a[0]) fatal("label expected");
904 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isspace(*a); ++a, ++p) *p = *a;
905 for (; p > res && isspace(p[-1]); --p) ;
906 *p = '\0';
907 if (p-res > 120) fatal("label name too long: %s", res);
908 while (*a && isspace(*a)) ++a;
909 if (*a) {
910 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
911 memmove(curLine, a, strlen(a)+1);
912 } else {
913 curLine[0] = '\0';
915 return res;
919 static char *getOneLabelArg (void) {
920 char *res = getLabelArg(1);
922 if (curLine[0] && curLine[0] != ':') fatal("too many expressions");
923 return res;
927 /* res == 0: end of expression */
928 static int skipComma (void) {
929 char *a = strSkipSpaces(curLine);
931 if (!a[0]) { curLine[0] = '\0'; return 0; }
932 if (a[0] == ':') {
933 while (a[0] && (a[0] == ':' || isspace(a[0]))) ++a;
934 if (!a[0]) { curLine[0] = '\0'; return 0; }
935 memmove(curLine, a, strlen(a)+1);
936 return -1;
938 if (a[0] != ',') fatal("invalid expression: ',' expected");
939 for (++a; *a && isspace(*a); ++a) ;
940 if (!a[0]) { curLine[0] = '\0'; return 0; }
941 memmove(curLine, a, strlen(a)+1);
942 return 1;
946 // ???
947 static void skipInstruction (void) {
948 char *str = curLine;
950 for (int cnt = 1; cnt > 0; --cnt) {
951 while (*str && isspace(*str)) ++str; // skip spaces
952 while (*str && !isspace(*str)) ++str; // skip non-spaces
953 while (*str && isspace(*str)) ++str; // skip spaces
954 if (!str[0] || *str != ':') break;
955 // try again if we have a colon
957 memmove(curLine, str, strlen(str)+1);
961 ///////////////////////////////////////////////////////////////////////////////
962 // label processor
964 static MAYBE_UNUSED void removeSpacesAndColons (void) {
965 char *ep = strSkipSpacesColons(curLine);
967 memmove(curLine, ep, strlen(ep)+1);
971 static void checkExprEnd (void) {
972 char *ep = strSkipSpaces(curLine);
974 memmove(curLine, ep, strlen(ep)+1);
975 if (curLine[0] && curLine[0] != ':') fatal("end of expression expected");
979 /* remove label from curLine */
980 static void removeLabel (void) {
981 char *ep = curLine;
983 if (ep[0] && !isspace(ep[0])) for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ; // skip text
984 // skip spaces and colons
985 ep = strSkipSpacesColons(ep);
986 memmove(curLine, ep, strlen(ep)+1);
990 static void processLabel (void) {
991 char *argstart;
992 char *ep, *ln, *nn;
993 static char n2[256];
994 UrLabelInfo *lbl;
995 int noLocAff = 0, doEQU = 0;
997 memset(n2, 0, sizeof(n2));
998 if (!curLine[0] || isspace(curLine[0]) || curLine[0] == ':') { removeLabel(); return; } // removeLabel() removes any spaces, etc
999 // collect label
1000 for (ep = curLine; *ep && !isspace(*ep) && *ep != ':'; ++ep) ;
1001 if (ep-curLine > 120) fatal("label too long");
1002 // copy label
1003 memset(n2, 0, sizeof(n2));
1004 memmove(n2, curLine, ep-curLine);
1005 if (!urIsValidLabelName(n2)) return; // this is not a valid label, get out of here
1006 // check if this can be instruction
1007 ep = strSkipSpaces(ep);
1008 if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1009 // ok, we got a good label
1010 removeLabel();
1011 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1012 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1013 lbl = urAddLabel(nn);
1014 if (!lbl->refFile) {
1015 lbl->refLine = curSrcLine->lineNo;
1016 lbl->refFile = strdup(curSrcLine->fname);
1018 //printf("new: [%s]\n", lbl->name);
1019 // get command name
1020 if (curLine[0] == '=') {
1021 doEQU = 0;
1022 argstart = curLine+1;
1023 } else {
1024 doEQU = 1;
1025 argstart = strIsCommand("EQU", curLine);
1027 if (!argstart || doEQU) {
1028 if (pass == 0 && lbl->type != -1) fatal("duplicate label %s", lbl->name);
1030 if (argstart) {
1031 // do '=' or 'EQU'
1032 memmove(curLine, argstart, strlen(argstart)+1);
1033 if (!doEQU) {
1034 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label %s", lbl->name);
1037 int defined = 1, addr = UR_FIXUP_NONE;
1038 int32_t res = getOneExprArg(&defined, &addr);
1040 lbl->type = doEQU;
1041 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1042 if (defined) {
1043 lbl->value = res;
1044 lbl->known = 1;
1045 } else {
1046 if (pass != 0) fatal("can't calculate label %s", lbl->name);
1048 curLine[0] = '\0';
1049 return;
1051 // code label
1052 if (lbl->name[0] != '{' && !noLocAff) {
1053 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
1054 lastSeenGlobalLabel = strdup(lbl->name);
1056 lbl->type = 2;
1057 lbl->value = disp;
1058 lbl->known = 1;
1059 lbl->fixuptype = UR_FIXUP_WORD;
1063 ///////////////////////////////////////////////////////////////////////////////
1064 // instruction finder (in source)
1066 /* array ends with NULL */
1067 /* returns line or NULL */
1068 /* iidx will be set to found instruction number */
1069 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
1070 if (iidx) *iidx = -1;
1071 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
1072 va_list ap;
1074 //if (!isspace(cur->line[0])) continue; // fuck labeled strings
1075 va_start(ap, iidx);
1076 for (int f = 0; ;++f) {
1077 const char *name = va_arg(ap, const char *);
1079 if (!name) break;
1080 if (strIsCommand(name, cur->line)) {
1081 va_end(ap);
1082 if (iidx) *iidx = f;
1083 return cur;
1086 va_end(ap);
1088 return NULL;
1092 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
1093 return findNextInstructionFromList(NULL, name, NULL);
1097 ///////////////////////////////////////////////////////////////////////////////
1098 // writers
1100 static int optWriteFixups = 0;
1101 static int optRunTape = 1;
1102 static int optRunDMB = 1;
1103 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1104 static int optSNA48 = 1;
1107 ///////////////////////////////////////////////////////////////////////////////
1108 static void writeFixups (void) {
1109 FILE *fo = fopen("zfixuptable.txt", "w");
1111 if (fo == NULL) fatal("can't write fixup file");
1112 fprintf(fo, "; addr dadr sz\n");
1113 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1114 static const char type[4] = "NWLH";
1115 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
1117 fclose(fo);
1121 ///////////////////////////////////////////////////////////////////////////////
1122 /* return 'found' flag */
1123 static int findChunkFrom (int addr, int *start, int *len) {
1124 if (addr < 0) addr = 0;
1125 for (; addr <= 65535 && !memused[addr]; ++addr) ;
1126 if (addr > 65535) return 0;
1127 *start = addr;
1128 for (; addr <= 65535 && memused[addr]; ++addr) ;
1129 *len = addr-(*start);
1130 return 1;
1134 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
1135 for (; buflen >= 2; buflen -= 2) {
1136 int cnt = *buf++;
1137 uint8_t byte = *buf++;
1139 if (cnt == 0) {
1140 // copy
1141 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
1142 if (!memused[addr]) putByte(addr, *buf);
1143 if (addr == 0xffff) return;
1145 } else {
1146 // fill
1147 for (; cnt > 0; --cnt, ++addr) {
1148 if (!memused[addr]) putByte(addr, byte);
1149 if (addr == 0xffff) return;
1156 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1157 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1158 if (bxor) *bxor = (*bxor)^b;
1159 return 0;
1163 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1164 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1165 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1166 return 0;
1170 ///////////////////////////////////////////////////////////////////////////////
1171 // .sna
1173 static int saveSna (const char *fname, int as48) {
1174 char *fn = malloc(strlen(fname)+16);
1175 uint8_t regs[27];
1176 FILE *fo;
1177 char abuf[32];
1179 sprintf(fn, "%s.sna", fname);
1180 fo = fopen(fn, "wb");
1181 free(fn);
1182 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1183 printf("out: %s.sna\n", fname);
1184 memmove(regs, ursna48, 27);
1185 #if 0
1186 if (optRunSNA) {
1187 /* push new address */
1188 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1189 sp -= 2;
1190 putByte(sp, ent&0xFFU);
1191 putByte(sp+1, (ent>>8)&0xFFU);
1192 regs[23] = sp&0xFFU;
1193 regs[24] = (sp>>8)&0xFFU;
1195 fwrite(regs, 27, 1, fo);
1196 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1197 fwrite(memory+16384, 49152, 1, fo);
1198 #endif
1200 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1202 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1203 //fprintf(stderr, "%d\n", memused[sp]);
1204 if (memused[sp] || memused[(sp+1)&0xffff]) {
1205 fprintf(stderr, "FATAL: can't save snapshot!\n");
1206 goto error;
1209 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
1210 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
1211 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1213 sprintf(abuf, "%05d", clrAddr);
1214 memcpy(memory+23762, abuf, 5);
1215 sprintf(abuf, "%05d", ent);
1216 memcpy(memory+23773, abuf, 5);
1218 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
1220 if (!as48) {
1221 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
1223 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
1224 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
1225 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
1226 // write pages
1227 memset(memory, 0, 16384);
1228 for (int f = 1; f < 8; ++f) {
1229 if (f != 2 && f != 5) {
1230 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
1235 fclose(fo);
1236 return 0;
1237 error:
1238 fclose(fo);
1239 unlink(fname);
1240 return -1;
1244 ///////////////////////////////////////////////////////////////////////////////
1245 // bin chunks
1247 static void saveRaw (const char *basename) {
1248 char *fname = malloc(strlen(basename)+16);
1249 int start = 0, len;
1250 FILE *fo;
1252 while (findChunkFrom(start, &start, &len)) {
1253 sprintf(fname, "%s_%04X.%s", basename, start, optTapExt?"tap":"bin");
1254 fo = fopen(fname, "wb");
1255 if (!fo) {
1256 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1257 } else {
1258 printf("out: %s\n", fname);
1259 if (fwrite(memory+start, len, 1, fo) != 1) {
1260 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1261 fclose(fo);
1262 unlink(fname);
1263 } else {
1264 fclose(fo);
1267 start += len;
1269 free(fname);
1273 ///////////////////////////////////////////////////////////////////////////////
1274 // HoBeta files
1276 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
1277 char name[8];
1278 char type;
1279 uint16_t start;
1280 uint16_t len;
1281 uint8_t zero; // always zero
1282 uint8_t secLen; // length in sectors
1283 uint16_t checksum;
1284 } HOBHeader;
1287 static uint16_t calcHobSum (const void *hdr) {
1288 const uint8_t *buf = (const uint8_t *)hdr;
1289 uint16_t res = 0;
1291 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
1292 return res;
1296 static void saveHob (const char *basename) {
1297 char *fname = malloc(strlen(basename)+16);
1298 int start = 0, len;
1299 HOBHeader hdr;
1300 FILE *fo;
1302 while (findChunkFrom(start, &start, &len)) {
1303 sprintf(fname, "%s_%04X.%s", basename, start, "$C");
1304 fo = fopen(fname, "wb");
1305 if (!fo) {
1306 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1307 start += len;
1308 } else {
1309 printf("out: %s\n", fname);
1310 memset(&hdr, 0, sizeof(hdr));
1311 memset(&hdr.name, 32, 8);
1312 sprintf(hdr.name, "%c%04X", basename[0], start);
1313 hdr.name[strlen(hdr.name)] = 32;
1314 hdr.type = 'C';
1315 hdr.start = start;
1316 hdr.len = len;
1317 hdr.secLen = (len+255)/256;
1318 hdr.checksum = calcHobSum(&hdr);
1319 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
1320 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1321 fclose(fo);
1322 unlink(fname);
1323 goto quit;
1325 if (fwrite(memory+start, len, 1, fo) != 1) {
1326 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1327 fclose(fo);
1328 unlink(fname);
1329 goto quit;
1331 start += len;
1332 while (len%256) {
1333 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
1334 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1335 fclose(fo);
1336 unlink(fname);
1337 goto quit;
1339 ++len;
1340 //fprintf(stderr, ":%d\n", len%256);
1342 fclose(fo);
1345 quit:
1346 free(fname);
1350 ///////////////////////////////////////////////////////////////////////////////
1351 // .dmb
1353 static int saveDMB (const char *fname) {
1354 char *fn = malloc(strlen(fname)+16);
1355 int start = 0, len;
1356 uint16_t pcnt = 0;
1357 FILE *fo;
1359 sprintf(fn, "%s.dmb", fname);
1360 fo = fopen(fn, "wb");
1361 free(fn);
1362 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
1364 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
1366 printf("out: %s.dmb\n", fname);
1367 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
1368 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
1369 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
1370 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
1372 start = 0;
1373 while (findChunkFrom(start, &start, &len)) {
1374 if (fWriteWord(fo, len, NULL) != 0) goto error;
1375 if (fWriteWord(fo, start, NULL) != 0) goto error;
1376 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
1377 start += len;
1379 fclose(fo);
1380 return 0;
1381 error:
1382 fclose(fo);
1383 unlink(fname);
1384 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
1385 return -1;
1389 ///////////////////////////////////////////////////////////////////////////////
1390 // .tap
1392 static char tapeLoaderName[16];
1395 static void saveTapCargador (FILE *fo) {
1396 // count blocks
1397 static uint8_t cargador[16384]; // should be enough for everyone
1398 int start = 0, len, pos, f;
1399 uint8_t bxor;
1402 void putStr (const char *s) {
1403 for (; *s; ++s) cargador[pos++] = *s;
1406 void putNum (int num) {
1407 char buf[64];
1408 sprintf(buf, "%d", num);
1409 putStr(buf);
1413 pos = 4;
1414 // number
1415 cargador[0] = 0; cargador[1] = 10;
1416 // size (will be fixed later)
1417 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
1418 // load blocks
1419 while (findChunkFrom(start, &start, &len)) {
1420 // :LOAD "" CODE
1421 putStr(":\xef\"\"\xaf");
1422 start += len;
1424 // and run
1425 // :RANDOMIZE USR VAL "xxx"
1426 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
1427 // patch len
1428 cargador[2] = (pos-4)&0xff;
1429 cargador[3] = ((pos-4)>>8)&0xff;
1430 // write header
1431 fWriteWord(fo, 19, NULL); // length of header
1432 bxor = 0;
1433 fWriteByte(fo, 0, &bxor); // header block
1434 fWriteByte(fo, 0, &bxor); // 'basic' flag
1435 if (tapeLoaderName[0]) {
1436 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1437 } else {
1438 fWriteByte(fo, 'c', &bxor);
1439 fWriteByte(fo, 'a', &bxor);
1440 fWriteByte(fo, 'r', &bxor);
1441 fWriteByte(fo, 'g', &bxor);
1442 fWriteByte(fo, 'a', &bxor);
1443 fWriteByte(fo, 'd', &bxor);
1444 fWriteByte(fo, 'o', &bxor);
1445 fWriteByte(fo, 'r', &bxor);
1446 fWriteByte(fo, ' ', &bxor);
1447 fWriteByte(fo, ' ', &bxor);
1449 fWriteWord(fo, pos, &bxor); // length
1450 fWriteWord(fo, 10, &bxor); // start
1451 fWriteWord(fo, pos, &bxor); // length2
1452 fWriteByte(fo, bxor, NULL);
1453 // write data
1454 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
1455 bxor = 0;
1456 fWriteByte(fo, 0xFFU, &bxor); // data block
1457 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
1458 fWriteByte(fo, bxor, NULL);
1462 static void saveTap (const char *basename) {
1463 char *fname = malloc(strlen(basename)+16);
1464 char blkname[128];
1465 int start = 0, len, f;
1466 uint8_t bxor;
1467 FILE *fo;
1469 sprintf(fname, "%s.tap", basename);
1470 fo = fopen(fname, "wb");
1471 free(fname);
1472 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
1473 printf("out: %s.tap\n", basename);
1474 if (optRunTape) saveTapCargador(fo);
1475 while (findChunkFrom(start, &start, &len)) {
1476 // write header
1477 if (tapeLoaderName[0]) {
1478 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
1479 memcpy(blkname, tapeLoaderName, 10);
1480 } else {
1481 sprintf(blkname, "c%04X:%04X", start, len);
1483 //printf(" block: %s\n", blkname);
1484 fWriteWord(fo, 19, NULL); // length of header
1485 bxor = 0;
1486 fWriteByte(fo, 0, &bxor); // header block
1487 fWriteByte(fo, 3, &bxor); // 'code' flag
1488 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
1489 fWriteWord(fo, len, &bxor);
1490 fWriteWord(fo, start, &bxor);
1491 fWriteWord(fo, 32768, &bxor);
1492 fWriteByte(fo, bxor, NULL);
1493 // write data
1494 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
1495 bxor = 0;
1496 fWriteByte(fo, 0xFFU, &bxor); // data block
1497 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
1498 fWriteByte(fo, bxor, NULL);
1499 start += len;
1501 fclose(fo);
1505 ///////////////////////////////////////////////////////////////////////////////
1506 // pseudoinstructions
1508 // note that processCurrentLine() will NOT skip to the next line before
1509 // calling pseudoinstruction handler!
1510 // note that processCurrentLine() will skip current line after calling
1511 // pseudoinstruction handler!
1513 static int wasOrg = 0;
1514 static int wasClr = 0;
1517 ///////////////////////////////////////////////////////////////////////////////
1518 // user warnings
1520 static int piDISPLAYX (int passNo, int asHex) {
1521 for (;;) {
1522 if (isStrArg()) {
1523 int len = 0;
1524 char *res = getStrArg(&len);
1526 if (passNo < 0 || pass == passNo) printf("%s", res);
1527 } else {
1528 int defined = 1;
1529 int32_t v = getExprArg(&defined, NULL);
1531 if (passNo < 0 || pass == passNo) {
1532 if (asHex) printf("%04X", (unsigned int)v);
1533 else printf("%d", v);
1536 if (!skipComma()) break;
1538 return 0;
1542 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
1543 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
1544 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
1545 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
1546 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
1547 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
1550 ///////////////////////////////////////////////////////////////////////////////
1551 // ORG, DISP, etc.
1553 static int piORG (void) {
1554 int defined = 1;
1555 int32_t res = getOneExprArg(&defined, NULL);
1557 if (!defined) fatal("sorry, ORG operand value must be known here");
1558 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
1559 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
1560 pc = disp = res;
1561 if (!wasOrg) {
1562 wasOrg = 1;
1563 ent = res;
1564 if (!wasClr && res > 0) clrAddr = res-1;
1566 return 0;
1570 static int piDISP (void) {
1571 int defined = 1;
1572 int32_t res = getOneExprArg(&defined, NULL);
1574 if (!defined) fatal("sorry, DISP operand value must be known here");
1575 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
1576 //printf("DISP=%d\n", res);
1577 disp = res;
1578 return 0;
1582 static int piENDDISP (void) {
1583 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
1584 checkExprEnd();
1585 disp = pc;
1586 return 0;
1590 static int piENT (void) {
1591 int defined = 1;
1592 int32_t res = getOneExprArg(&defined, NULL);
1594 //if (!defined) fatal("sorry, ENT operand value must be known here");
1595 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
1596 ent = res;
1597 return 0;
1601 static int piCLR (void) {
1602 int defined = 1;
1603 int32_t res = getOneExprArg(&defined, NULL);
1605 //if (!defined) fatal("sorry, CLR operand value must be known here");
1606 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
1607 clrAddr = res;
1608 wasClr = 1;
1609 return 0;
1613 static int piRESERVE (void) {
1615 int defined = 1, start;
1616 int32_t res = getExprArg(&defined, NULL);
1617 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1618 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1619 start = res;
1620 if (!skipComma()) fatal("RESERVE needs 2 args");
1621 res = getOneExprArg(&defined, NULL);
1622 if (!defined) fatal("sorry, RESERVE operand values must be known here");
1623 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
1624 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
1626 fatal("RESERVE: not yet!");
1627 return 0;
1631 static int piALIGN (void) {
1632 int defined = 1;
1633 int32_t res;
1635 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
1636 res = getOneExprArg(&defined, NULL);
1637 if (!defined) fatal("sorry, ALIGN operand value must be known here");
1638 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
1639 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
1640 if (res > 0 && pc%res != 0) {
1641 pc /= res;
1642 pc *= res;
1643 pc += res;
1644 disp = pc;
1646 return 0;
1650 static int piDISPALIGN (void) {
1651 int defined = 1;
1652 int32_t res = getOneExprArg(&defined, NULL);
1654 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
1655 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
1656 if (res > 0 && disp%res != 0) {
1657 disp /= res;
1658 disp *= res;
1659 disp += res;
1661 return 0;
1665 ///////////////////////////////////////////////////////////////////////////////
1666 // DEFx
1668 static int defIncr = 0;
1671 static int piDEFINCR (void) {
1672 int defined = 1;
1673 int32_t cnt = getOneExprArg(&defined, NULL);
1675 if (!defined) fatal("DEFINCR: increment must be defined");
1676 defIncr = cnt;
1677 return 0;
1681 static int piDEFBW (int isWord) {
1682 for (;;) {
1683 int defined = 0, fixuptype = UR_FIXUP_NONE;
1685 if (isStrArg()) {
1686 int f, len = 0;
1687 char *res = getStrArg(&len);
1689 for (f = 0; f < len; ++f) {
1690 int32_t b = (uint8_t)res[f];
1691 b += defIncr;
1692 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
1693 if (b < 0) b += 256;
1694 emitByte(b);
1696 } else {
1697 int32_t res = getExprArg(&defined, &fixuptype);
1699 if (pass > 0 && !defined) fatal("undefined operand");
1700 res += defIncr;
1701 if (isWord) {
1702 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
1703 if (res < 0) res += 65536;
1704 if (fixuptype != UR_FIXUP_NONE) urAsmFixupOperandFn(NULL, pc, disp, fixuptype, 2);
1705 if (isWord == 1) emitWord(res); else emitRWord(res);
1706 } else {
1707 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
1708 if (fixuptype != UR_FIXUP_NONE) {
1709 if (fixuptype == UR_FIXUP_WORD) fatal("invalid fixup for operand");
1710 urAsmFixupOperandFn(NULL, pc, disp, fixuptype, 1);
1712 if (res < 0) res += 256;
1713 emitByte(res);
1716 if (!skipComma()) break;
1718 return 0;
1721 static int piDEFB (void) { return piDEFBW(0); }
1722 static int piDEFW (void) { return piDEFBW(1); }
1723 static int piDEFR (void) { return piDEFBW(2); }
1726 static int piDEFS (void) {
1727 for (;;) {
1728 int32_t bt, f;
1729 int defined = 0, fixuptype = UR_FIXUP_NONE;
1730 int32_t res = getExprArg(&defined, &fixuptype);
1732 if (pass > 0 && !defined) fatal("undefined operand");
1733 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
1734 if (*curLine && curLine[0] == ',') {
1735 skipComma();
1736 bt = getExprArg(&defined, NULL);
1737 if (pass > 0 && !defined) fatal("undefined operand");
1738 bt += defIncr;
1739 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
1740 if (bt < 0) bt += 256;
1741 if (fixuptype != UR_FIXUP_NONE) {
1742 if (fixuptype == UR_FIXUP_WORD) fatal("invalid fixup for operand");
1744 for (f = 0; f < res; ++f) {
1745 if (fixuptype != UR_FIXUP_NONE) urAsmFixupOperandFn(NULL, pc, disp, fixuptype, 1);
1746 emitByte(bt);
1748 } else {
1749 pc += res; disp += res;
1751 if (!skipComma()) break;
1753 return 0;
1757 /* bit 0: put '\0' */
1758 /* bit 1: set bit 7 of last byte */
1759 /* bit 2: put length */
1760 static int piDEFSTR (int type) {
1761 for (;;) {
1762 if (isStrArg()) {
1763 int f, len = 0;
1764 char *res = getStrArg(&len);
1766 if (type&0x04) {
1767 if (len > 255) fatal("string too long");
1768 emitByte(len);
1770 for (f = 0; f < len; ++f) {
1771 uint8_t b = (uint8_t)res[f];
1773 if ((type&0x02) && f == len-1) b |= 0x80;
1774 emitByte(b);
1776 if (type&0x01) emitByte(0);
1777 } else {
1778 int defined = 1;
1779 int32_t v = getExprArg(&defined, NULL);
1781 if (pass > 0 && !defined) fatal("undefined expression");
1782 if (!defined) v = 0; else v += defIncr;
1783 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
1784 if (v < 0) v += 256;
1785 emitByte(v);
1787 if (!skipComma()) break;
1789 return 0;
1793 static int piDEFM (void) { return piDEFSTR(0x00); }
1794 static int piDEFZ (void) { return piDEFSTR(0x01); }
1795 static int piDEFX (void) { return piDEFSTR(0x02); }
1796 static int piDEFC (void) { return piDEFSTR(0x04); }
1799 ///////////////////////////////////////////////////////////////////////////////
1800 // INCBIN
1802 /* INCBIN "name"[,maxlen] */
1803 static int piINCBIN (void) {
1804 int system = 0;
1805 char *fn, qCh;
1806 uint8_t bt;
1807 FILE *fl;
1808 int maxlen = 65536;
1809 char *args = curLine;
1811 if (!curLine[0]) fatal("INCBIN without file name");
1812 if (isStrArg()) {
1813 qCh = *args++;
1814 system = 0;
1815 } else if (curLine[0] == '<') {
1816 qCh = '<'; ++args;
1817 system = 1;
1818 } else {
1819 qCh = 0;
1820 system = 0;
1822 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
1823 if (!fn[0]) fatal("INCBIN: empty file name");
1824 memmove(curLine, args, strlen(args)+1);
1825 // maxlen
1826 if (curLine[0] == ',') {
1827 int defined = 1;
1829 skipComma();
1830 maxlen = getOneExprArg(&defined, NULL);
1831 if (!defined) fatal("INCBIN: undefined maxlen");
1832 if (maxlen < 1) return 1; // nothing to do
1834 // now fix name
1835 if (system) {
1836 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
1837 } else {
1838 sprintf(curLine, "%s", fn);
1841 fl = fopen(curLine, "rb");
1842 if (!fl) fatal("INCBIN: file not found: %s", curLine);
1843 while (maxlen-- > 0) {
1844 int res = fread(&bt, 1, 1, fl);
1846 if (!res) break;
1847 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: %s", curLine); }
1848 emitByte(bt);
1850 fclose(fl);
1851 return 1;
1855 ///////////////////////////////////////////////////////////////////////////////
1856 // MODULE, ENDMODULE
1858 static int piENDMODULE (void) {
1859 if (!curModule) fatal("ENDMODULE without MODULE");
1860 if (curLine[0]) {
1861 char *mn = getOneLabelArg();
1863 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
1865 curModule = NULL;
1866 return 0;
1870 static int piMODULE (void) {
1871 ModuleInfo *mi;
1872 char *mn;
1873 SourceLine *ol = curSrcLine;
1874 int inum;
1876 if (curModule) fatal("no nested modules allowed");
1877 mn = getOneLabelArg();
1878 if (!urIsValidLabelName(mn)) fatal("invalid module name: %s", mn);
1879 mi = moduleFind(mn);
1880 //printf("[%s] %p\n", mn, mi);
1881 if (mi) {
1882 if (mi->seen) {
1883 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
1884 /* skip module */
1885 nextSrcLine(); // skip ourself
1886 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
1887 setCurSrcLine(ol);
1888 fatal("no ENDMODULE");
1890 if (inum == 0) fatal("no nested modules allowed");
1891 curModule = mi;
1892 skipInstruction();
1893 piENDMODULE();
1894 return 0;
1896 } else {
1897 mi = moduleAdd(mn, curSrcLine->fname);
1899 mi->seen = 1;
1900 curModule = mi;
1901 return 0;
1905 ///////////////////////////////////////////////////////////////////////////////
1906 // DUP, EDUP
1908 static int piEDUP (void) {
1909 fatal("EDUP without DUP");
1910 checkExprEnd();
1911 return 1;
1915 static int piDUP (void) {
1916 int defined = 1, dupCnt = 1, inum;
1917 SourceLine *stline, *eline = NULL;
1918 int32_t cnt = getOneExprArg(&defined, NULL);
1920 if (!defined) fatal("DUP: counter must be defined");
1921 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
1922 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
1923 // now find corresponding EDUP
1924 // note that we should skip nested DUPs
1925 nextSrcLine(); // skip ourself
1926 stline = curSrcLine;
1927 while (curSrcLine) {
1928 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
1929 // ok, we found something; what is it?
1930 if (inum == 0) {
1931 // new DUP, skip it
1932 ++dupCnt;
1933 nextSrcLine(); // skip DUP
1934 } else {
1935 // EDUP
1936 if (--dupCnt == 0) {
1937 // gotcha!
1938 eline = curSrcLine;
1939 break;
1941 nextSrcLine(); // skip EDUP
1944 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
1945 // now repeat that lines
1946 while (cnt-- > 0) {
1947 setCurSrcLine(stline);
1948 while (curSrcLine != eline) processCurrentLine();
1950 return 1;
1954 ///////////////////////////////////////////////////////////////////////////////
1955 // IF, ENDIF
1957 static int ifCount = 0;
1960 static int ifSkipToEndIfOrElse (int wholeBody) {
1961 int inum, wasElse = 0;
1962 SourceLine *oline;
1964 while (curSrcLine) {
1965 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
1966 switch (inum) {
1967 case 0: /* if */
1968 case 6: /* ifx */
1969 nextSrcLine(); // skip IF
1970 ifSkipToEndIfOrElse(1); // and recurse
1971 nextSrcLine(); // skip ENDIF
1972 break;
1973 case 1: /* else */
1974 if (wasElse) fatal("duplicate ELSE");
1975 if (!wholeBody) return 1;
1976 wasElse = 1;
1977 nextSrcLine(); // skip ELSE
1978 break; // and continue
1979 case 2: /* endif */
1980 return 0; // and exit
1981 case 3: /* elif */
1982 case 7: /* elifx */
1983 if (wasElse) fatal("ELSEIF in ELSE");
1984 if (!wholeBody) return 2;
1985 nextSrcLine(); // skip ELSEIF
1986 break; // and continue
1987 case 4: /* macro */
1988 // skip it as a whole
1989 nextSrcLine(); // skip MACRO
1990 for (;;) {
1991 oline = curSrcLine;
1992 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
1993 if (inum == 1) break;
1994 fatal("invalid nested MACRO");
1996 nextSrcLine(); // skip ENDM
1997 break;
1998 case 5: /* endm */
1999 fatal("unexpected ENDM");
2002 fatal("IF without ENDIF");
2003 return -1;
2007 static int piENDIF (void) {
2008 if (--ifCount < 0) fatal("ENDIF without IF");
2009 checkExprEnd();
2010 return 1;
2014 static int piELSE (void) {
2015 if (--ifCount < 0) fatal("ELSE without IF");
2016 nextSrcLine(); // skip ELSE
2017 ifSkipToEndIfOrElse(1);
2018 return 1;
2022 static int piELSEIF (void) {
2023 if (--ifCount < 0) fatal("ELSEIF without IF");
2024 nextSrcLine(); // skip ELSEIF
2025 ifSkipToEndIfOrElse(1);
2026 return 1;
2030 static int piIFAll (int isIfX) {
2031 int defined = 1;
2032 int ooo = lblOptMakeU2;
2033 lblOptMakeU2 = isIfX ? 1 : 0;
2034 int32_t cond = getOneExprArg(&defined, NULL);
2035 lblOptMakeU2 = ooo;
2037 if (!defined) {
2038 if (!isIfX) fatal("IF: condition must be defined");
2039 cond = 0; // for IFX: 0 is there is any undefined label
2041 if (cond) {
2042 // ok, do it until ELSE/ELSEIF/ENDIF
2043 ++ifCount;
2044 return 1;
2046 for (;;) {
2047 int r;
2048 char *args;
2050 nextSrcLine(); // skip last instruction
2051 // skip until ELSE/ELSEIF/ENDIF
2052 r = ifSkipToEndIfOrElse(0);
2053 if (r == 0) break; // ENDIF
2054 if (r == 1) { ++ifCount; break; } // ELSE
2055 // ELSEIF, do condition
2056 if ((args = strIsCommand("ELSEIF", curLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
2057 memmove(curLine, args, strlen(args)+1);
2058 cond = getOneExprArg(&defined, NULL);
2059 if (!defined) fatal("ELSEIF: condition must be defined");
2060 if (cond) { ++ifCount; break; } // condition is true
2062 return 1;
2066 static int piIF (void) { return piIFAll(0); }
2067 static int piIFX (void) { return piIFAll(1); }
2070 ///////////////////////////////////////////////////////////////////////////////
2071 // macro processor
2073 * what i did with MACRO is the brain-damaged cheating all the way.
2074 * first, i will collect the MACRO body and remember it.
2075 * second, when the macro is used, i will:
2076 * * create unique labels for all supplied macro args, each with
2077 * number (so IFARG/IFNARG can check the existance)
2078 * * insert the whole macro body in place, fixing argument refs
2079 * * let the asm play with it
2080 * another tricky part is 'local labels': i have to change all
2081 * '..lbl' references -- generate new label name for it and
2082 * replace all refs. and be careful to not touch the strings.
2083 * this is not the best scheme, but it is fairly simple and it works.
2085 // will be called when parser encounters term starting with '=' or '*'
2086 // first term char will not be skipped
2087 // must return pointer to the first char after expression end
2088 static const char *getValueCB (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2089 char name[257], *p = name;
2091 if (curmacro == NULL) fatal("'=' outside of macro");
2092 if (*expr++ != '=') fatal("'=' expected!");
2093 //fprintf(stderr, "GV: [%s]\n", expr);
2095 expr = strSkipSpaces(expr);
2096 while (*expr) {
2097 if (isalnum(*expr) || *expr == '_') {
2098 if (p-name > 250) fatal("id too long");
2099 *p++ = *expr++;
2100 continue;
2102 break;
2104 *p = 0;
2105 expr = strSkipSpaces(expr);
2106 for (int f = 0; f < curmacro->mac->argc; ++f) {
2107 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
2108 if (*expr == '[') {
2109 URExprValue v;
2110 int l = strlen(curmacro->argvals[f]);
2112 ++expr;
2113 urInitExprValue(&v);
2114 expr = urExpressionEx(&v, expr, addr, &donteval, defined, error);
2115 if (*error) return expr;
2116 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
2117 ++expr;
2118 if (v.val < 0) v.val += l;
2119 if (v.val < 0 || v.val >= l) {
2120 res->val = '?';
2121 } else {
2122 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
2124 return expr;
2125 } else {
2126 urExpressionEx(res, curmacro->argvals[f], addr, &donteval, defined, error);
2127 //fprintf(stderr, "GV: [%s] [%s] [%d]\n", name, curmacro->argvals[f], res->val);
2128 return expr;
2132 fatal("unknown macro variable: '%s'", name);
2136 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
2137 static int expandCB (char *opr, int oprlen) {
2138 char name[257], *p = name, *op = opr;
2140 if (curmacro == NULL) fatal("'=' outside of macro");
2141 if (*op++ != '=') fatal("'=' expected!");
2142 //fprintf(stderr, "expand: [%s]\n", opr);
2143 //fatal("!!!");
2144 op = strSkipSpaces(op);
2145 while (*op) {
2146 if (isalnum(*op) || *op == '_') {
2147 if (p-name > 250) fatal("id too long");
2148 *p++ = *op++;
2149 continue;
2151 break;
2153 *p = 0;
2154 op = strSkipSpaces(op);
2155 for (int f = 0; f < curmacro->mac->argc; ++f) {
2156 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
2157 if (*op == '[') {
2158 URExprValue v;
2159 int l = strlen(curmacro->argvals[f]), error = 0, defined = 1, donteval = 0;
2161 ++op;
2162 urInitExprValue(&v);
2163 op = (char *)urExpressionEx(&v, op, pc, &donteval, &defined, &error);
2164 if (error) return -1;
2165 if (op == NULL || *op != ']' || v.str != NULL) return -1;
2166 op = strSkipSpaces(op+1);
2167 if (*op) return -1;
2168 if (v.val < 0) v.val += l;
2169 if (v.val < 0 || v.val >= l) {
2170 strcpy(opr, "?");
2171 } else {
2172 opr[0] = curmacro->argvals[f][v.val];
2173 opr[1] = 0;
2175 return 0;
2178 strncpy(opr, curmacro->argvals[f], oprlen);
2179 opr[oprlen-1] = 0;
2180 return (*op ? -1 : 0);
2183 fatal("unknown macro variable: '%s'", name);
2187 static void processMacro (MacroDef *mc) {
2188 SourceLine *oldcurline = curSrcLine;
2189 CurMacroDef cm;
2191 //fprintf(stderr, "processMacro: [%s]\n", mc->name);
2192 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
2194 //parse macro arguments
2196 cm.mac = mc;
2197 for (int f = 0; f < mc->argc; ++f) {
2198 char *e, ech;
2200 skipSpaces();
2202 if (!curLine[0]) {
2203 for (int c = f; c < mc->argc; ++c) {
2204 if (mc->argdef[c] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[c]);
2205 cm.argvals[c] = strdup(mc->argdef[c]);
2207 break;
2210 if (curLine[0] == ',') {
2211 if (mc->argdef[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
2212 cm.argvals[f] = strdup(mc->argdef[f]);
2213 memmove(curLine, curLine+1, strlen(curLine));
2214 continue;
2217 e = skipArg(curLine);
2218 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
2219 if ((ech = *e) != 0) {
2220 if (f == mc->argc-1 || ech != ',') fatal("invalid macro invocation");
2222 *e = 0;
2223 cm.argvals[f] = strdup(curLine);
2224 if (ech) {
2225 *e = ech;
2226 memmove(curLine, e+1, strlen(e));
2227 } else {
2228 curLine[0] = 0;
2232 skipSpaces();
2233 if (curLine[0]) fatal("invalid macro invocation");
2235 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
2237 setCurSrcLine(mc->lines);
2238 curmacro = &cm;
2239 ++curmacronum;
2241 while (curSrcLine != NULL) {
2242 //fprintf(stderr, "*[%s]\n", curSrcLine->line);
2243 processCurrentLine();
2246 setCurSrcLine(oldcurline);
2247 curmacro = NULL;
2248 nextSrcLine();
2249 //fatal("!!!");
2250 //return 1;
2254 static MacroDef *findMacro (const char *name) {
2255 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strcasecmp(name, mc->name) == 0) return mc;
2256 return NULL;
2260 static int piMACRO (void) {
2261 char *name;
2262 int argc = 0;
2263 char *argdef[32];
2264 char *argnames[32];
2265 SourceLine *stline, *eline = NULL;
2266 MacroDef *mc;
2268 name = strdup(getLabelArg(0));
2269 //fprintf(stderr, "[%s]\n", name);
2270 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
2271 if (curLine[0] == ',') memmove(curLine, curLine+1, strlen(curLine));
2272 skipSpaces();
2274 while (curLine[0]) {
2275 if (argc >= 31) fatal("too many arguments in MACRO");
2276 if (!isalpha(curLine[0])) fatal("invalid MACRO definition");
2278 argnames[argc] = strdup(getLabelArg(0));
2279 if (curLine[0] == '=') {
2280 // default value
2281 char *e = strchr(curLine, ','), tch;
2283 if (e == NULL) e = curLine+strlen(curLine);
2284 tch = *e;
2285 *e = 0;
2286 argdef[argc] = strdup(curLine+1);
2287 *e = tch;
2288 memmove(curLine, e, strlen(e)+1);
2289 } else {
2290 argdef[argc] = NULL;
2292 skipSpaces();
2293 if (curLine[0] == ',') {
2294 memmove(curLine, curLine+1, strlen(curLine));
2295 skipSpaces();
2297 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdef[argc]);
2298 ++argc;
2301 // now find corresponding ENDM
2302 // note that we should skip nested DUPs
2303 stline = curSrcLine;
2304 nextSrcLine(); // skip ourself
2305 while (curSrcLine) {
2306 int inum;
2308 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
2309 // ok, we found something; what is it?
2310 if (inum == 0) {
2311 // new MACRO
2312 fatal("no nested MACROs yet");
2313 } else {
2314 // ENDM, gotcha!
2315 eline = curSrcLine;
2316 // kill ENDM
2317 eline->line[0] = 0;
2318 nextSrcLine(); // skip ENDM
2319 break;
2322 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
2324 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
2325 mc->name = name;
2326 mc->argc = argc;
2327 for (int f = 0; f < argc; ++f) { mc->argdef[f] = argdef[f]; mc->argnames[f] = argnames[f]; }
2330 if ((tline = calloc(1, sizeof(*tline))) == NULL) fatal("out of memory");
2331 tline->next = stline->next;
2332 tline->line = strdup(stline->line);
2333 tline->fname = strdup(stline->fname);
2334 tline->lineNo = stline->lineNo;
2336 eline->next = NULL;
2337 mc->lines = stline->next;
2338 stline->next = curSrcLine;
2339 stline->line[0] = 0;
2341 mc->next = maclist;
2342 maclist = mc;
2343 //fatal("sorry, no MACRO yet");
2344 return 1;
2348 static int piENDM (void) {
2349 fatal("ENDM without MACRO");
2350 return 1;
2354 ///////////////////////////////////////////////////////////////////////////////
2355 // line processor
2356 static int optWriteType = 't';
2357 static int optWTChanged = 0;
2360 static void piTapParseLoaderName (void) {
2361 if (skipComma()) {
2362 int len;
2363 char *fn;
2365 if (!isStrArg()) fatal("loader name expected");
2366 fn = getStrArg(&len);
2367 if (len > 10) fatal("loader name too long");
2368 memset(tapeLoaderName, ' ', 10);
2369 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
2374 static int piDEFFMT (void) {
2375 char *name;
2377 if (isStrArg()) {
2378 int len = 0;
2380 name = getStrArg(&len);
2381 } else {
2382 name = getLabelArg(1);
2384 if (optWTChanged) return 1;
2385 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA")) {
2386 optRunTape = 0;
2387 //optRunSNA = (toupper(name[0]) == 'R');
2388 optWriteType = 's';
2389 if (curLine[0]) fatal("too many expressions");
2390 return 1;
2392 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
2393 optRunTape = 0;
2394 //optRunSNA = 0;
2395 optWriteType = 't';
2396 piTapParseLoaderName();
2397 return 1;
2399 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE")) {
2400 optRunTape = 1;
2401 //optRunSNA = 0;
2402 optWriteType = 't';
2403 piTapParseLoaderName();
2404 return 1;
2406 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
2407 optRunTape = 0;
2408 //optRunSNA = 0;
2409 optWriteType = 'r';
2410 if (curLine[0]) fatal("too many expressions");
2411 return 1;
2413 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB")) {
2414 optRunTape = 0;
2415 //optRunSNA = 0;
2416 optRunDMB = (toupper(name[0]) == 'R');
2417 optWriteType = 'd';
2418 if (curLine[0]) fatal("too many expressions");
2419 return 1;
2421 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
2422 optRunTape = 0;
2423 //optRunSNA = 0;
2424 optWriteType = 'n';
2425 if (curLine[0]) fatal("too many expressions");
2426 return 1;
2428 fatal("invalid default output type: %s", name);
2432 ///////////////////////////////////////////////////////////////////////////////
2433 // line processor
2435 static void processCurrentLine (void) {
2436 if (!curSrcLine) return; // do nothing
2437 loadCurSrcLine();
2438 processLabel();
2439 for (;;) {
2440 char *str, *ee, name[66];
2441 UrAsmOp *op;
2442 int len;
2443 const char *errpos;
2445 removeSpacesAndColons();
2446 // skip spaces and ':'
2447 if (!curLine[0]) { nextSrcLine(); break; }
2448 // try to find and process command
2449 str = curLine; //while (*str && isspace(*str)) ++str; // skip spaces
2450 // find command end
2451 for (ee = str; *ee && !isspace(*ee) && *ee != '"' && *ee != '\''; ++ee) ;
2452 // get command, if any
2453 if (ee != str && ee-str <= 64) {
2454 MacroDef *mc;
2456 memset(name, 0, sizeof(name));
2457 memmove(name, str, ee-str);
2458 // known command?
2459 op = urFindOp(name);
2460 if (op) {
2461 // ok, do it
2462 str = ee; while (*str && isspace(*str)) ++str; // skip spaces
2463 memmove(curLine, str, strlen(str)+1);
2464 if (op->fn()) {
2465 nextSrcLine(); // skip it
2466 break;
2468 continue;
2470 if ((mc = findMacro(name)) != NULL) {
2471 // ok, do it
2472 str = ee; while (*str && isspace(*str)) ++str; // skip spaces
2473 memmove(curLine, str, strlen(str)+1);
2475 processMacro(mc);
2476 //FIXME: allow ':' here?
2477 break;
2481 len = urAssembleOne(curLine, pc, disp, &errpos);
2482 if (len < 0) fatalUrLib(len);
2483 pc += len; disp += len;
2484 if (len >= 0 && errpos) {
2485 memmove(curLine, errpos+1, strlen(errpos));
2486 } else {
2487 nextSrcLine(); // skip it
2488 break;
2494 ///////////////////////////////////////////////////////////////////////////////
2495 // setup instructions
2497 static void registerInstructions (void) {
2498 urAddOp("DISPLAY", piDISPLAY);
2499 urAddOp("DISPLAY0", piDISPLAY0);
2500 urAddOp("DISPLAYA", piDISPLAYA);
2501 urAddOp("DISPHEX", piDISPHEX);
2502 urAddOp("DISPHEX0", piDISPHEX0);
2503 urAddOp("DISPHEXA", piDISPHEXA);
2505 urAddOp("DEFFMT", piDEFFMT);
2507 urAddOp("MACRO", piMACRO);
2508 urAddOp("ENDM", piENDM);
2510 urAddOp("ORG", piORG);
2511 urAddOp("DISP", piDISP);
2512 urAddOp("ENDDISP", piENDDISP);
2513 urAddOp("PHASE", piDISP);
2514 urAddOp("DEPHASE", piENDDISP);
2515 urAddOp("UNPHASE", piENDDISP);
2516 urAddOp("ALIGN", piALIGN);
2517 urAddOp("DISPALIGN", piDISPALIGN);
2518 urAddOp("PHASEALIGN", piDISPALIGN);
2519 urAddOp("ENT", piENT);
2520 urAddOp("CLR", piCLR);
2521 urAddOp("RESERVE", piRESERVE);
2523 urAddOp("INCBIN", piINCBIN);
2525 urAddOp("MODULE", piMODULE);
2526 urAddOp("ENDMODULE", piENDMODULE);
2528 urAddOp("DUP", piDUP);
2529 urAddOp("EDUP", piEDUP);
2531 urAddOp("IF", piIF);
2532 urAddOp("IFX", piIFX);
2533 urAddOp("ELSE", piELSE);
2534 urAddOp("ELSEIF", piELSEIF);
2535 urAddOp("ELSEIFX", piELSEIF);
2536 urAddOp("ENDIF", piENDIF);
2538 urAddOp("DEFINCR", piDEFINCR);
2539 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
2540 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
2541 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
2542 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
2543 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
2544 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
2545 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
2546 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
2550 ///////////////////////////////////////////////////////////////////////////////
2551 // !0: invalid label
2553 static inline void fnSkipSpaces (const char *expr) {
2554 while (*expr && isspace(*expr)) ++expr;
2555 return expr;
2560 static inline char fnNextChar (const char *expr) {
2561 while (*expr && isspace(*expr)) ++expr;
2562 return *expr;
2566 #define FN_CHECK_END do { \
2567 while (*expr && isspace(*expr)) ++expr; \
2568 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
2569 ++expr; \
2570 } while (0)
2573 #define FN_CHECK_COMMA do { \
2574 while (*expr && isspace(*expr)) ++expr; \
2575 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
2576 ++expr; \
2577 } while (0)
2580 static const char *readLabelName (char *buf, const char *expr) {
2581 int pos = 0;
2583 while (*expr && isspace(*expr)) ++expr;
2584 for (;;) {
2585 char ch = *expr++;
2587 if (pos >= 128) return NULL;
2588 if (!ch) break;
2589 if (isalnum(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
2590 buf[pos++] = ch;
2591 } else {
2592 break;
2595 if (pos < 1) return NULL;
2596 buf[pos] = '\0';
2597 if (!urIsValidLabelName(buf)) return NULL;
2598 return expr;
2602 static const char *fnDefKn (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
2603 char lbl[130];
2605 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
2606 FN_CHECK_END;
2607 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
2608 return expr;
2611 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); }
2612 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); }
2615 static const char *fnAligned256 (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2616 expr = urExpressionEx(res, expr, addr, &donteval, defined, error);
2617 FN_CHECK_END;
2618 if (!donteval) res->val = (res->val%256 ? 0 : 1);
2619 return expr;
2623 static const char *fnSameSeg (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2624 URExprValue v0, v1;
2626 urInitExprValue(&v0);
2627 urInitExprValue(&v1);
2628 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2629 if (*error) return expr;
2630 FN_CHECK_COMMA;
2631 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2632 if (*error) return expr;
2633 FN_CHECK_END;
2634 if (!donteval) res->val = (v0.val/256 == v1.val/256);
2635 return expr;
2639 static const char *fnAlign (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2640 URExprValue v0, v1;
2642 urInitExprValue(&v0);
2643 urInitExprValue(&v1);
2644 expr = urExpressionEx(&v0, expr, addr, &donteval, defined, error);
2645 if (*error) return expr;
2646 if (fnNextChar(expr) == ',') {
2647 FN_CHECK_COMMA;
2648 expr = urExpressionEx(&v1, expr, addr, &donteval, defined, error);
2649 if (*error) return expr;
2650 } else {
2651 v1.val = 256;
2653 FN_CHECK_END;
2654 if (!donteval) {
2655 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
2656 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
2658 return expr;
2662 static const char *fnLow (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2663 const char *ee = expr;
2665 expr = urExpressionEx(res, expr, addr, &donteval, defined, error);
2666 FN_CHECK_END;
2667 if (!donteval) {
2668 if (res->fixuptype == UR_FIXUP_HIBYTE) {
2669 *error = UR_EXPRERR_FUNC; return ee;
2671 res->fixuptype = UR_FIXUP_LOBYTE;
2672 res->val &= 0xff;
2674 return expr;
2678 static const char *fnHigh (URExprValue *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2679 const char *ee = expr;
2681 expr = urExpressionEx(res, expr, addr, &donteval, defined, error);
2682 FN_CHECK_END;
2683 if (!donteval) {
2684 if (res->fixuptype == UR_FIXUP_LOBYTE) {
2685 *error = UR_EXPRERR_FUNC; return ee;
2687 res->fixuptype = UR_FIXUP_HIBYTE;
2688 res->val = (res->val>>8)&0xff;
2690 return expr;
2694 static void registerFunctions (void) {
2695 urExpressionRegisterFunction("defined", fnDefined);
2696 urExpressionRegisterFunction("known", fnKnown);
2697 urExpressionRegisterFunction("aligned256", fnAligned256);
2698 urExpressionRegisterFunction("align", fnAlign);
2699 urExpressionRegisterFunction("sameseg", fnSameSeg);
2700 urExpressionRegisterFunction("low", fnLow);
2701 urExpressionRegisterFunction("high", fnHigh);
2705 ///////////////////////////////////////////////////////////////////////////////
2706 // preparing another pass
2708 static void initPass (void) {
2709 curSrcLine = asmText;
2710 curModule = NULL;
2711 pc = disp = ent = 0x100; // viva CP/M!
2712 inTapeBlock = 0;
2713 tapeXorB = 0;
2714 wasOrg = 0;
2715 wasClr = 0;
2716 ifCount = 0;
2717 defIncr = 0;
2718 lblOptMakeU2 = 0;
2719 curmacronum = 0;
2720 lastSeenGlobalLabel = strdup(" [MAIN] ");
2721 modulesResetSeen();
2722 prepareMemory();
2726 static int posstPass (void) {
2727 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
2728 if (checkLabels()) return -1;
2729 if (ifCount != 0) fatal("unbalanced IFs");
2730 return 0;
2734 ///////////////////////////////////////////////////////////////////////////////
2735 // options
2737 static struct option longOpts[] = {
2738 {"sna", 0, NULL, 's'},
2739 {"sna128", 0, NULL, 'S'},
2740 {"tap", 0, NULL, 't'},
2741 {"autotap", 0, NULL, 'T'},
2742 /*{"rawtap", 0, NULL, 'T'},*/
2743 {"raw", 0, NULL, 'r'},
2744 {"autodmb", 0, NULL, 'B'},
2745 {"dmb", 0, NULL, 'b'},
2746 {"none", 0, NULL, 'n'},
2747 {"help", 0, NULL, 'h'},
2748 {"hob", 0, NULL, 'H'},
2749 {"fixups", 0, NULL, 'H'},
2750 {NULL, 0, NULL, 0}
2754 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
2757 static void usage (const char *pname) {
2758 printf(
2759 "usage: %s [options] infile\n"
2760 "default infiles:", pname);
2761 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
2762 printf("\n"
2763 "options:\n"
2764 " -s --sna write 48K .SNA file with autostart\n"
2765 " -S --sna128 write 148K .SNA file with autostart\n"
2766 " -t --tap write .tap file\n"
2767 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
2768 " -r --raw write raw file(s)\n"
2769 " -b --dmb write DMB file\n"
2770 " -B --autodmb write DMB file with autostart\n"
2771 " -H --hob write HoBeta code file(s)\n"
2772 " -n --none write nothing\n"
2773 " -F --fixups write fixup file 'zfixuptable.txt'\n"
2774 " -h --help this help\n");
2778 ///////////////////////////////////////////////////////////////////////////////
2779 // main
2781 int main (int argc, char *argv[]) {
2782 int res = 0, c;
2783 const char *pname = argv[0];
2784 char *inFile = NULL;
2785 initInclideDir();
2787 urGetByteFn = getByte;
2788 urPutByteFn = putByte;
2789 urFindLabelByNameFn = findLabelCB;
2790 urGetValueFn = getValueCB;
2791 urExpandFn = expandCB;
2792 //urIsLabelDefinedOrKnownFn = isLabelDefinedOrKnown;
2793 urAsmFixupOperandFn = fixupOperandCB;
2795 //strcpy(tapeLoaderName, "cargador ");
2796 tapeLoaderName[0] = 0;
2798 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
2799 while ((c = getopt_long(argc, argv, "sStTbBrnhHF", longOpts, NULL)) >= 0) {
2800 switch (c) {
2801 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
2802 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
2803 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
2804 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
2805 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
2806 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
2807 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
2808 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
2809 case 'h': usage(pname); res = 0; goto earlyerrquit;
2810 case 'F': optWriteFixups = 1; break;
2811 case '?': return 1;
2814 if (optind >= argc) {
2815 // try to find default input file
2816 int f;
2817 for (f = 0; defInFiles[f]; ++f) {
2818 if (!access(defInFiles[f], R_OK)) {
2819 inFile = strdup(defInFiles[f]);
2820 break;
2823 } else {
2824 inFile = strdup(argv[optind]);
2826 if (!inFile || !inFile[0]) {
2827 res = 1;
2828 fprintf(stderr, "ERROR: no input file!\n");
2829 goto earlyerrquit;
2832 registerInstructions();
2833 registerFunctions();
2835 res = asmTextLoad(inFile);
2836 if (!res) {
2838 printf("dumping...\n");
2839 FILE *fo = fopen("z000.out", "w");
2840 if (fo) {
2841 SourceLine *c;
2842 for (c = asmText; c; c = c->next) fprintf(fo, "%s ; %s: %d\n", c->line, c->fname, c->lineNo);
2843 fclose(fo);
2846 for (pass = 0; pass <= 1; ++pass) {
2847 initPass();
2848 printf("pass %d\n", pass);
2849 setCurSrcLine(asmText);
2850 if (setjmp(errJP)) { res = 1; break; }
2851 while (curSrcLine) processCurrentLine();
2852 if (posstPass()) { res = 1; break; }
2854 // write result
2855 if (res == 0) {
2856 char *oc = strdup(inFile);
2857 char *pd = strrchr(oc, '.');
2858 if (pd && !strchr(oc, '/')) *pd = '\0';
2859 switch (optWriteType) {
2860 case 's': saveSna(oc, optSNA48); break;
2861 case 't': saveTap(oc); break;
2862 case 'r': saveRaw(oc); break;
2863 case 'd': saveDMB(oc); break;
2864 case 'H': saveHob(oc); break;
2866 free(oc);
2867 if (optWriteFixups) writeFixups();
2869 } else {
2870 fprintf(stderr, "ERROR: loading error!\n");
2873 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
2874 clearFixups();
2875 urClearLabels();
2876 modulesClear();
2877 asmTextClear();
2878 urClearOps();
2879 earlyerrquit:
2880 if (inFile) free(inFile);
2881 if (sysIncludeDir) free(sysIncludeDir);
2882 return res?1:0;