winsys sample: comsetix
[urasm.git] / src / urasm.c
blob7560f55e833693530ab02a83e9ba4dc68d9ec0f3
1 // URASM Z80 assembler
2 // coded by Ketmar // Invisible Vector
3 // GPLv3 or later
4 //
5 #include <getopt.h>
6 #include <setjmp.h>
7 #include <stdarg.h>
8 #include <stddef.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 #ifdef WIN32
20 # include <windows.h>
21 #endif
23 #include "liburasm/liburasm.h"
25 #include "ursna48.c"
28 #define VERSION_HI 0
29 #define VERSION_MID 2
30 #define VERSION_LO 2
33 #define MAYBE_UNUSED __attribute__((unused))
35 #define lambda(return_type, body_and_args) ({ \
36 return_type __fn__ body_and_args \
37 __fn__; \
41 ////////////////////////////////////////////////////////////////////////////////
42 static inline int isSpace (char ch) { return (ch && ((unsigned)(ch&0xff) <= 32 || ch == 127)); }
43 static inline int isAlpha (char ch) { return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')); }
44 static inline int isDigit (char ch) { return (ch >= '0' && ch <= '9'); }
45 static inline int isHexDigit (char ch) { return ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')); }
46 static inline int isAlphaDigit (char ch) { return (isAlpha(ch) || isDigit(ch)); }
48 static inline char toUpper (char ch) { return (ch >= 'a' && ch <= 'z' ? ch-'a'+'A' : ch); }
49 static inline char toLower (char ch) { return (ch >= 'A' && ch <= 'Z' ? ch-'A'+'a' : ch); }
52 ////////////////////////////////////////////////////////////////////////////////
53 static char *strprintfVA (const char *fmt, va_list vaorig) {
54 char *buf = NULL;
55 int olen, len = 128;
57 buf = malloc(len);
58 if (buf == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
59 for (;;) {
60 char *nb;
61 va_list va;
63 va_copy(va, vaorig);
64 olen = vsnprintf(buf, len, fmt, va);
65 va_end(va);
66 if (olen >= 0 && olen < len) return buf;
67 if (olen < 0) olen = len*2-1;
68 nb = realloc(buf, olen+1);
69 if (nb == NULL) { fprintf(stderr, "\nFATAL: out of memory!\n"); abort(); }
70 buf = nb;
71 len = olen+1;
76 static __attribute((format(printf,1,2))) char *strprintf (const char *fmt, ...) {
77 char *buf = NULL;
78 va_list va;
80 va_start(va, fmt);
81 buf = strprintfVA(fmt, va);
82 va_end(va);
83 return buf;
87 ///////////////////////////////////////////////////////////////////////////////
88 // global variables
90 static char *sysIncludeDir = NULL;
91 static char *refFileName = NULL;
93 #define MAX_LINE_SIZE 16384
94 static char currLine[MAX_LINE_SIZE]; /* current processing line; can be freely modified */
97 ///////////////////////////////////////////////////////////////////////////////
98 // init dirs
100 static void initInclideDir (void) {
101 const char *id = getenv("URASM_INCLUDE_DIR");
102 if (id && id[0]) {
103 sysIncludeDir = strdup(id);
104 } else {
105 char myDir[4096];
106 memset(myDir, 0, sizeof(myDir));
107 #ifndef WIN32
108 if (readlink("/proc/self/exe", myDir, sizeof(myDir)-1) < 0) {
109 strcpy(myDir, ".");
110 } else {
111 char *p = (char *)strrchr(myDir, '/');
112 if (!p) strcpy(myDir, "."); else *p = '\0';
114 #else
115 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
116 char *p = strrchr(myDir, '\\');
117 if (!p) strcpy(myDir, "."); else *p = '\0';
118 #endif
119 strcat(myDir, "/libs");
120 sysIncludeDir = strdup(myDir);
122 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
123 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
127 ///////////////////////////////////////////////////////////////////////////////
128 // string utilities
130 /* trim trailing spaces and comments; normalize colons */
131 static void normalizeStr (char *s) {
132 char *p = s;
133 //int paren = 0;
134 // now skip all shit
135 while (*p) {
136 const char ch = *p++;
137 /* check for "af'" */
138 if (ch == '\'' && p-s >= 2 && toLower(p[-2] == 'a') && toLower(p[-1] == 'f')) continue;
139 /* parens */
140 //if (ch == '(') { ++paren; continue; }
141 //if (ch == ')') { if (--paren < 0) paren = 0; continue; }
142 /* comment */
143 if (ch == ';') { p[-1] = 0; break; }
144 /* string */
145 if (ch == '"' || ch == '\'') {
146 const char qch = ch;
147 while (*p) {
148 const char c1 = *p++;
149 if (c1 == qch) break;
150 if (c1 == '\\' && *p) ++p;
152 continue;
154 /* reduce and normalise colons */
155 if (/*paren == 0 &&*/ ch == ':') {
156 --p; /* back to colon */
157 /* remove spaces before colon */
158 char *t = p;
159 while (t != s && isSpace(t[-1])) --t;
160 if (t != p) memmove(t, p, strlen(p)+1);
161 p = t;
162 if (p[0] != ':') abort(); // assert
163 ++p; /* skip colon */
164 /* remove following spaces and colons */
165 t = p;
166 while (*t == ':' || isSpace(*t)) ++t;
167 if (t != p) memmove(p, t, strlen(t)+1);
168 continue;
171 /* done; trim trailing spaces and colons */
172 size_t slen = strlen(s);
173 while (slen > 0 && (isSpace(s[slen-1]) || s[slen-1] == ':')) --slen;
174 s[slen] = 0;
178 /* check if string starts with the given command (case-insensitive) */
179 /* returns NULL or pointer to args */
180 /* skips spaces after command if any */
181 static char *strIsCommand (const char *command, char *str) {
182 for (int cnt = 1; cnt > 0; --cnt) {
183 while (*str && isSpace(*str)) ++str; // skip spaces
184 for (; *command && *str; ++command, ++str) {
185 if (toUpper(*command) != toUpper(*str)) return NULL; // alas
187 if (*command) return NULL; // alas
188 if (*str && isAlphaDigit(*str)) return NULL; // alas
189 while (*str && isSpace(*str)) ++str; // skip spaces
190 if (*str && *str == ':') break; // try again if we have a colon
191 return str; // found
193 return NULL;
197 /* parse string literal */
198 /* don't free() result */
199 /* skips trailing spaces */
200 static char *parseStr (char **str, char endQ, int *lenp) {
201 static char buf[MAX_LINE_SIZE];
202 int len = 0, n, f, base;
203 char *a = *str;
205 int xDigit (char ch, int base) {
206 if (ch < '0') return -1;
207 if (base <= 10) {
208 if (ch >= '0'+base) return -1;
209 return ch-'0';
211 ch = toUpper(ch);
212 if (ch <= '9') return ch-'0';
213 if (ch < 'A' || ch > 'A'+base-10) return -1;
214 ch -= 'A'-10;
215 return (ch < base ? ch : -1);
218 memset(buf, 0, sizeof(buf));
219 if (lenp) *lenp = 0;
220 for (; *a; ++a) {
221 if (*a == '\\') {
222 if (!a[1]) break;
223 switch (*(++a)) {
224 case 'a': buf[len++] = '\a'; break;
225 case 'b': buf[len++] = '\b'; break;
226 case 'e': buf[len++] = '\x1b'; break;
227 case 'f': buf[len++] = '\f'; break;
228 case 'n': buf[len++] = '\n'; break;
229 case 'r': buf[len++] = '\r'; break;
230 case 't': buf[len++] = '\t'; break;
231 case 'v': buf[len++] = '\v'; break;
232 case 'z': buf[len++] = '\0'; break;
233 case 'x': case 'X': // hex
234 ++a; // skip 'x'
235 base = 16; f = 2;
236 donum: for (n = 0; f > 0; --f) {
237 char ch = xDigit(*a++, base);
239 if (ch < 0) { --a; break; }
240 n *= base;
241 n += ch;
243 buf[len++] = n;
244 --a; // return to the last digit, 'for' will skip it
245 break;
246 case '0': // octal
247 base = 8; f = 4;
248 goto donum;
249 case '1' ... '9': // decimal
250 base = 10; f = 3;
251 goto donum;
252 default: buf[len++] = a[0]; break; // others
254 } else {
255 if (*a == endQ) { ++a; break; }
256 buf[len++] = *a;
259 while (*a && isSpace(*a)) ++a; // skip trailing spaces
260 *str = a;
261 buf[len] = '\0';
262 if (lenp) *lenp = len;
263 return buf;
267 ///////////////////////////////////////////////////////////////////////////////
268 // source file stack, reader, etc
270 typedef struct SourceLine {
271 struct SourceLine *next;
272 char *line;
273 char *fname;
274 int lineNo;
275 } SourceLine;
277 static SourceLine *asmText = NULL;
278 static SourceLine *asmTextLast = NULL;
279 static SourceLine *curSrcLine = NULL;
281 #define MAX_MACRO_ARGS (32)
283 typedef struct MacroDef {
284 struct MacroDef *next;
285 char *name;
286 SourceLine *lines;
287 int argc;
288 char *argdefaults[MAX_MACRO_ARGS]; // default values
289 char *argnames[MAX_MACRO_ARGS]; // argument names
290 } MacroDef;
292 typedef struct {
293 MacroDef *mac;
294 char *argvals[MAX_MACRO_ARGS]; // argument values
295 } CurMacroDef;
297 static MacroDef *maclist = NULL;
298 static CurMacroDef *curmacro = NULL; // !NULL if we are not inserting macro
299 static int curmacronum = 0;
302 static MacroDef *findMacro (const char *name) {
303 for (MacroDef *mc = maclist; mc != NULL; mc = mc->next) if (strcasecmp(name, mc->name) == 0) return mc;
304 return NULL;
308 static void asmTextClear (void) {
309 while (asmText) {
310 SourceLine *l = asmText;
312 asmText = asmText->next;
313 free(l->line);
314 free(l->fname);
315 free(l);
317 asmTextLast = curSrcLine = NULL;
321 static int asmTextLoad (const char *fname) {
322 FILE *fl;
323 int lineNo = 0;
324 SourceLine *s;
326 if (!(fl = fopen(fname, "r"))) {
327 fprintf(stderr, "ERROR: can't open file: %s\n", fname);
328 return -1;
330 printf("loading: %s\n", fname);
331 // read file
332 while (fgets(currLine, sizeof(currLine)-1, fl)) {
333 ++lineNo;
334 currLine[sizeof(currLine)-1] = '\0';
335 normalizeStr(currLine);
336 //fprintf(stderr, "*[%s]\n", curLine);
337 if (!currLine[0]) continue; // don't store empty lines
338 // add current line
339 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
340 s->lineNo = lineNo;
341 if ((s->line = strdup(currLine)) == NULL) abort();
342 if ((s->fname = strdup(fname)) == NULL) abort();
343 if (asmTextLast) asmTextLast->next = s; else asmText = s;
344 asmTextLast = s;
346 fclose(fl);
347 return 0;
351 static inline void loadCurSrcLine (void) { if (curSrcLine) strcpy(currLine, (curSrcLine != NULL ? curSrcLine->line : "")); }
352 static inline SourceLine *setCurSrcLine (SourceLine *l) { curSrcLine = l; loadCurSrcLine(); return l; }
353 static inline SourceLine *nextSrcLine (void) { return (curSrcLine != NULL ? setCurSrcLine(curSrcLine->next) : NULL); }
356 static int includeCount = 0;
359 static void normIncName (char *dest, const char *fn, int system) {
360 struct stat st;
362 if (system) sprintf(dest, "%s/%s", sysIncludeDir, fn); else sprintf(dest, "%s", fn);
363 if (stat(dest, &st)) return;
364 if (S_ISDIR(st.st_mode)) strcat(dest, "/zzmain.zas");
368 // process 'INCLUDE'
369 // include file instead of the current line
370 static int asmTextInclude (const char *fname, int system) {
371 char *fn;
372 FILE *fl;
373 int lineNo = 0;
374 SourceLine *first = NULL, *last = NULL, *s = NULL;
376 if (includeCount > 256) {
377 fprintf(stderr, "ERROR: file %s, line %d: too many nested INCLUDEs!\n", curSrcLine->fname, curSrcLine->lineNo);
378 return -1;
381 fn = strdup(fname);
382 normIncName(currLine, fn, system);
383 free(fn);
384 fn = alloca(strlen(currLine+1));
385 strcpy(fn, currLine);
386 ++includeCount;
387 printf("loading: %s\n", fn);
388 if ((fl = fopen(fn, "r")) == NULL) {
389 fprintf(stderr, "ERROR: file %s, line %d: can't open INCLUDE file: '%s'\n", curSrcLine->fname, curSrcLine->lineNo, currLine);
390 return -1;
393 while (fgets(currLine, sizeof(currLine)-1, fl)) {
394 ++lineNo;
395 currLine[sizeof(currLine)-1] = '\0';
396 normalizeStr(currLine);
397 if (!currLine[0]) continue; // don't store empty lines
398 // add current line
399 if ((s = calloc(1, sizeof(SourceLine))) == NULL) abort();
400 s->lineNo = lineNo;
401 if ((s->line = strdup(currLine)) == NULL) abort();
402 if ((s->fname = strdup(fn)) == NULL) abort();
403 if (last != NULL) last->next = s; else first = s;
404 last = s;
406 fclose(fl);
407 --includeCount;
408 // now replace current line
410 free(curSrcLine->line);
411 free(curSrcLine->fname);
412 if (last != NULL) {
413 curSrcLine->line = first->line;
414 curSrcLine->fname = first->fname;
415 curSrcLine->lineNo = first->lineNo;
416 if ((first = first->next) != NULL) {
417 // more than one line
418 last->next = curSrcLine->next;
419 curSrcLine->next = first;
423 curSrcLine->line[0] = 0;
424 last->next = curSrcLine->next;
425 curSrcLine->next = first;
426 return 0;
430 ///////////////////////////////////////////////////////////////////////////////
431 // prototypes
433 static void processCurrentLine (void); // only one, will skip to next one
436 ///////////////////////////////////////////////////////////////////////////////
437 // error raisers, etc
439 static jmp_buf errJP;
442 static void errorWriteFile (FILE *fo) {
443 if (curSrcLine) {
444 fprintf(fo, "at file %s, line %d\n%s\n*", curSrcLine->fname, curSrcLine->lineNo, curSrcLine->line);
445 } else {
446 fprintf(fo, "somewhere in time: ");
450 static void errorMsgV (const char *fmt, va_list ap) {
451 errorWriteFile(stderr);
452 vfprintf(stderr, fmt, ap);
453 va_end(ap);
454 fputc('\n', stderr);
455 fflush(stderr);
459 static void __attribute__((format(printf, 1, 2))) warningMsg (const char *fmt, ...) {
460 va_list ap;
461 fprintf(stderr, "WARNING ");
462 va_start(ap, fmt);
463 errorMsgV(fmt, ap);
467 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
468 va_list ap;
469 fprintf(stderr, "FATAL ");
470 va_start(ap, fmt);
471 errorMsgV(fmt, ap);
475 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
476 va_list ap;
477 va_start(ap, fmt);
478 errorMsgV(fmt, ap);
479 longjmp(errJP, 666);
483 static void __attribute__((noreturn)) fatalUrLib (int errcode) {
484 errorMsg("%s", urasm_errormsg(errcode));
485 longjmp(errJP, 666);
489 //////////////////////////////////////////////////////////////////////////////
490 // operator management
492 // return !0 to skip current line
493 typedef int (*UrAsmOpFn) (void);
495 enum {
496 PI_CONT_LINE = 0,
497 PI_SKIP_LINE = 1
500 typedef struct UrAsmOp {
501 char *name;
502 UrAsmOpFn fn;
503 struct UrAsmOp *next;
504 } UrAsmOp;
506 static UrAsmOp *oplist = NULL;
509 static UrAsmOp *urAddOp (const char *name, UrAsmOpFn fn) {
510 UrAsmOp *res = calloc(1, sizeof(UrAsmOp));
511 if (!res) abort();
512 res->name = strdup(name);
513 res->fn = fn;
514 res->next = oplist;
515 oplist = res;
516 return res;
520 static UrAsmOp *urFindOp (const char *name) {
521 UrAsmOp *res;
522 for (res = oplist; res; res = res->next) if (!strcasecmp(name, res->name)) break;
523 return res;
527 static void urClearOps (void) {
528 while (oplist) {
529 UrAsmOp *c = oplist;
531 oplist = oplist->next;
532 free(c->name);
533 free(c);
538 ///////////////////////////////////////////////////////////////////////////////
539 // label management
541 typedef struct UrLabelInfo {
542 char *name;
543 int32_t value;
544 int type; /* -1: uknown (forward reference); 0: =; 1: equ; 2: code */
545 int known; /* !0: label value already known */
546 int refLine; /* first referenced line */
547 int fixuptype; /* UR_FIXUP_XXX */
548 char *refFile;
549 struct UrLabelInfo *next;
550 } UrLabelInfo;
552 static UrLabelInfo *labels = NULL;
555 static void urClearLabels (void) {
556 UrLabelInfo *c;
558 while ((c = labels) != NULL) {
559 labels = c->next;
560 if (c->name) free(c->name);
561 if (c->refFile) free(c->refFile);
562 free(c);
567 static UrLabelInfo *urFindLabel (const char *name) {
568 for (UrLabelInfo *c = labels; c; c = c->next) if (strcmp(name, c->name) == 0) return c;
569 return NULL;
573 static UrLabelInfo *urAddLabel (const char *name) {
574 UrLabelInfo *c = urFindLabel(name);
576 if (c == NULL) {
577 UrLabelInfo *p;
579 for (p = NULL, c = labels; c; p = c, c = c->next) {}
580 c = calloc(1, sizeof(UrLabelInfo));
581 if (!c) abort();
582 c->name = strdup(name);
583 c->type = -1;
584 c->fixuptype = UR_FIXUP_NONE;
585 if (p) p->next = c; else labels = c;
586 c->next = NULL;
588 return c;
592 ///////////////////////////////////////////////////////////////////////////////
593 // module list management
595 typedef struct ModuleInfo {
596 char *name;
597 char *fname; // opened in this file
598 int seen; // !0: module already seen, skip other definitions from the same file
599 struct ModuleInfo *next;
600 } ModuleInfo;
602 static ModuleInfo *modules = NULL;
603 static ModuleInfo *curModule = NULL;
606 static void modulesClear (void) {
607 curModule = NULL;
608 while (modules) {
609 ModuleInfo *c = modules;
611 modules = modules->next;
612 free(c->name);
613 free(c->fname);
614 free(c);
619 static void modulesResetSeen (void) {
620 for (ModuleInfo *c = modules; c; c = c->next) c->seen = 0;
624 static ModuleInfo *moduleFind (const char *name) {
625 if (!name || !name[0]) return NULL;
626 for (ModuleInfo *c = modules; c; c = c->next) if (!strcmp(c->name, name)) return c;
627 return NULL;
631 static ModuleInfo *moduleAdd (const char *name, const char *fname) {
632 ModuleInfo *c;
634 if (!name || !fname || !name[0] || !fname[0]) abort();
635 if ((c = calloc(1, sizeof(ModuleInfo))) == NULL) abort();
636 if ((c->name = strdup(name)) == NULL) abort();
637 if ((c->fname = strdup(fname)) == NULL) abort();
638 c->next = modules;
639 return (modules = c);
643 ///////////////////////////////////////////////////////////////////////////////
644 // fixup management
645 typedef struct FixupItem {
646 struct FixupItem *next;
647 uint16_t opdestaddr;
648 uint16_t opaddr;
649 int fixuptype;
650 int size;
651 } FixupItem;
652 static FixupItem *fixlisthead = NULL, *fixlisttail = NULL;
655 static void clearFixups (void) {
656 FixupItem *c;
658 while ((c = fixlisthead) != NULL) {
659 fixlisthead = c->next;
660 free(c);
665 static void addFixup (uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
666 FixupItem *fx = calloc(1, sizeof(FixupItem));
668 fx->opdestaddr = opdestaddr;
669 fx->opaddr = opaddr;
670 fx->fixuptype = fixuptype;
671 fx->size = size;
673 if (fixlisttail != NULL) fixlisttail->next = fx; else fixlisthead = fx;
674 fx->next = NULL;
675 fixlisttail = fx;
679 ///////////////////////////////////////////////////////////////////////////////
680 // destination memory management
682 static uint8_t memory[65536];
683 static char memused[65536];
684 static char memresv[65536];
685 static uint16_t start_pc = 0x100; // viva CP/M!
686 static uint16_t start_disp = 0x100; // viva CP/M!
687 static uint16_t start_ent = 0x100; // viva CP/M!
688 static uint16_t pc = 0; /* current position to write */
689 static uint16_t disp = 0; /* current 'virtual PC' */
690 static uint16_t ent = 0; /* starting address */
691 static uint16_t clrAddr = 24999; /* address for CLEAR for cargador */
692 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
693 static int inTapeBlock = 0;
694 static uint8_t tapeXorB = 0;
697 static inline uint8_t getByte (uint16_t addr) {
698 return memory[addr];
703 static inline __attribute__((unused)) uint16_t getWord (uint16_t addr) {
704 return ((uint16_t)memory[addr])|(((uint16_t)memory[addr+1])<<8);
709 static inline void putByte (uint16_t addr, uint8_t b) {
710 if (inTapeBlock) tapeXorB ^= b;
711 memory[addr] = b;
712 memused[addr] = 1;
716 static inline MAYBE_UNUSED void putWord (uint16_t addr, uint16_t w) {
717 putByte(addr, w&0xFFU);
718 putByte(addr+1, (w>>8)&0xFFU);
722 static inline void emitByte (uint8_t b) {
723 putByte(pc, b);
724 ++pc;
725 ++disp;
729 static inline void emitWord (uint16_t w) {
730 emitByte(w&0xFFU);
731 emitByte((w>>8)&0xFFU);
735 static inline void emitRWord (uint16_t w) {
736 emitByte((w>>8)&0xFFU);
737 emitByte(w&0xFFU);
741 static void prepareMemory (void) {
742 memset(memory, 0, sizeof(memory));
743 memset(memused, 0, sizeof(memused));
744 memset(memresv, 0, sizeof(memresv));
748 ///////////////////////////////////////////////////////////////////////////////
749 // label getter and utilities
751 static char *lastSeenGlobalLabel = NULL; /* global */
754 static char *fixLocalLabel (const char *name) {
755 static char newname[MAX_LINE_SIZE*2+1024];
757 memset(newname, 0, sizeof(newname));
758 if (!name || !name[0]) {
759 newname[0] = '\0';
760 } else if (!lastSeenGlobalLabel || name[0] != '.') {
761 strcpy(newname, name);
762 } else {
763 if (name[0] == '.' && name[1] == '.') {
764 // this is macro label
765 if (curmacro == NULL) fatal("macro label outside of macro: '%s'", name);
766 sprintf(newname, "{#MAC%d:%s:%s}", curmacronum, curmacro->mac->name, name);
767 } else {
768 // this is local label, let's rename it
769 sprintf(newname, "{%s%s}", lastSeenGlobalLabel, name);
771 /*!fprintf(stderr, "LABEL <%s> renamed to <%s> (%d)\n", name, newname, (curSrcLine ? curSrcLine->lineNo : 0));*/
773 return newname;
777 static char *fixGlobalLabel (const char *name) {
778 static char newname[MAX_LINE_SIZE*2+1024];
780 memset(newname, 0, sizeof(newname));
781 if (!name || !name[0]) {
782 newname[0] = '\0';
783 } else if (!curModule || name[0] == '.' || name[0] == '{' || name[0] == '@' || strchr(name, '.')) {
784 if (name[0] == '@' && name[1]) ++name;
785 strcpy(newname, name);
786 } else {
787 // this is global unqualified label and we have a module; let's rename it
788 sprintf(newname, "%s.%s", curModule->name, name);
790 //printf("%s --> %s\n", name, newname);
791 return newname;
795 static int lblOptMakeU2 = 0;
797 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found, int *fixuptype) {
798 UrLabelInfo *lbl;
799 char *ln, *nn;
801 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
802 lbl = urFindLabel(nn);
803 if (!lbl) {
804 // try non-module label
805 lbl = urFindLabel(ln);
807 if (!lbl) {
808 if (pass != 0) {
809 errorMsg("using undefined label %s", ln);
810 *found = 0;
811 *defined = 0;
812 return 0;
814 lbl = urAddLabel(nn);
815 lbl->type = (lblOptMakeU2 ? -42 : -1);
816 lbl->known = 0;
817 lbl->refLine = curSrcLine->lineNo;
818 lbl->refFile = strdup(curSrcLine->fname);
819 //printf("new label: [%s]\n", lbl->name);
820 } else {
821 //printf("label reference: [%s]\n", lbl->name);
823 if (lbl) {
824 *found = 1;
825 *defined = lbl->known!=0;
826 *fixuptype = lbl->fixuptype;
827 return lbl->value;
829 *found = 0;
830 *defined = 0;
831 return 0;
835 // qtypes
836 enum {
837 UR_QTYPE_DEFINED,
838 UR_QTYPE_KNOWN
841 static int isLabelDefinedOrKnown (const char *name, uint16_t addr, int qtype) {
842 UrLabelInfo *lbl;
843 char *ln, *nn;
845 nn = fixGlobalLabel((ln = fixLocalLabel(name)));
846 lbl = urFindLabel(nn);
847 if (!lbl) {
848 // try non-module label
849 lbl = urFindLabel(ln);
851 switch (qtype) {
852 case UR_QTYPE_DEFINED: return lbl ? lbl->known!=0 : 0;
853 case UR_QTYPE_KNOWN: return lbl!=NULL;
854 default: ;
856 return 0;
860 static void fixupOperandCB (const urasm_operand_t *op, uint16_t opdestaddr, uint16_t opaddr, int fixuptype, int size) {
861 if (pass == 1) {
862 //static const char *n[4] = {"none", "word", "low", "high"};
863 //fprintf(stderr, "%d: fixupOperandCB: destaddr=#%04x; addr=#%04x; pc=#%04X; fixuptype=%s\n", size, opdestaddr, opaddr, pc, n[fixuptype]);
864 addFixup(opdestaddr, opaddr, fixuptype, size);
869 static int checkLabels (void) {
870 int wasError = 0;
872 for (UrLabelInfo *c = labels; c; c = c->next) {
873 if (c->type == -1) {
874 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
875 wasError = 1;
877 if (c->type == 0) c->known = -1;
879 //if (wasError) longjmp(errJP, 667);
880 return wasError;
884 ///////////////////////////////////////////////////////////////////////////////
885 // expression utils (aka string parsing)
888 /* skip leading spaces */
889 /* returns string with spaces skipped */
890 static inline char *strSkipSpaces (const char *s) {
891 while (*s && isSpace(*s)) ++s;
892 return (char *)s;
896 /* skip leading spaces */
897 /* returns string with spaces skipped */
898 static inline const char *strSkipSpacesConst (const char *s) {
899 while (*s && isSpace(*s)) ++s;
900 return s;
904 /* remove trailing spaces from string */
905 static void strTrimRight (char *s) {
906 if (!s || !s[0]) return;
907 size_t len = strlen(s);
908 while (len > 0 && isSpace(s[len-1])) --len;
909 s[len] = 0;
913 /* skip leading spaces and colons */
914 /* returns string with spaces skipped */
915 static inline char *strSkipSpacesColons (char *s) {
916 while (*s && (isSpace(*s) || *s == ':')) ++s;
917 return s;
921 /* remove leading spaces from the current line */
922 static inline void removeSpaces (void) {
923 char *e = strSkipSpaces(currLine);
924 if (e != currLine) memmove(currLine, e, strlen(e)+1);
928 /* skip leading spaces, and argument (up to, but not including comma or colon) */
929 /* correctly skip strings */
930 /* returns string after skipped argument (with trailing spaces skipped) */
931 static char *skipMacroArg (char *str) {
932 int parens = 0;
933 char *strstart = str;
934 for (;;) {
935 str = strSkipSpaces(str);
936 if (!str[0]) return str;
937 if (parens == 0 && (str[0] == ',' || str[0] == ':')) return str;
938 /* check for "af'" */
939 if (str[0] == '\'' && str-strstart >= 2 && toLower(str[-2] == 'a') && toLower(str[-1] == 'f')) {
940 ++str;
941 continue;
943 if (str[0] == '(') { ++parens; continue; }
944 if (str[0] == ')') { if (--parens < 0) parens = 0; continue; }
945 /* check for string */
946 if (str[0] == '"' || str[0] == '\'') {
947 const char qch = *str++;
948 while (*str) {
949 const char ch = *str++;
950 if (ch == qch) break;
951 if (ch == '\\' && *str) ++str;
953 continue;
955 ++str;
960 /* evaluate next numeric expression in input string */
961 /* returns expression value */
962 static int32_t getExprArg (int *defined, int *addr) {
963 int error = 0;
964 char *a = strSkipSpaces(currLine);
965 int32_t res;
966 if (!a[0]) fatal("expression expected");
967 const char *ee = urasm_expr(&res, a, disp, defined, addr, &error);
968 if (error) fatalUrLib(error);
969 if (*ee) {
970 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
971 memmove(currLine, ee, strlen(ee)+1);
972 } else {
973 currLine[0] = '\0';
975 return res;
979 /* evaluate next string expression in input string */
980 /* returns expression value */
981 static char *getStrExprArg (void) {
982 int error = 0;
983 int donteval = 0, defined = 0;
984 static char resbuf[256];
985 char *a = strSkipSpaces(currLine);
986 if (!a[0]) fatal("expression expected");
987 urasm_exprval_t res;
988 urasm_exprval_init(&res);
989 const char *ee = urasm_expr_ex(&res, a, disp, &donteval, &defined, &error);
990 if (error) fatalUrLib(error);
991 if (*ee) {
992 if (ee[0] != ',' && ee[0] != ':') fatal("bad expression");
993 memmove(currLine, ee, strlen(ee)+1);
994 } else {
995 currLine[0] = '\0';
997 if (res.str) {
998 snprintf(resbuf, sizeof(resbuf), "%s", res.str);
999 } else {
1000 snprintf(resbuf, sizeof(resbuf), "%d", res.val);
1002 urasm_exprval_clear(&res);
1003 return resbuf;
1007 /* evaluate next numeric expression in input string */
1008 /* there shoild be no other expressions in the string */
1009 /* returns expression value */
1010 static int32_t getOneExprArg (int *defined, int *addr) {
1011 int32_t res = getExprArg(defined, addr);
1012 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
1013 return res;
1017 /* is next expression a string literal? */
1018 static inline int isStrArg (void) {
1019 const char *s = strSkipSpaces(currLine);
1020 return (s[0] == '"' || s[0] == '\'');
1024 /* check of we reached end of operator */
1025 static __attribute__((unused)) inline int isOperatorEnd (void) {
1026 const char *s = strSkipSpaces(currLine);
1027 return (s[0] == 0 || s[0] == ':');
1031 /* check of we reached end of operator */
1032 static __attribute__((unused)) inline int isLineEnd (void) {
1033 const char *s = strSkipSpaces(currLine);
1034 return (s[0] == 0);
1038 /* parse string argument from input string */
1039 /* returns parsed string */
1040 static char *getStrArg (int *lenp) {
1041 char *res, qCh;
1042 char *a = strSkipSpaces(currLine);
1043 qCh = *a++;
1044 if (qCh != '"' && qCh != '\'') fatal("string expected");
1045 res = parseStr(&a, qCh, lenp);
1046 if (*a) {
1047 if (a[0] != ',' && a[0] != ':') fatal("bad string expression");
1048 memmove(currLine, a, strlen(a)+1);
1049 } else {
1050 currLine[0] = '\0';
1052 return res;
1056 /* get identifier (and lowercase it) */
1057 static char *getOneIdArgLo (void) {
1058 static char res[MAX_LINE_SIZE+128];
1059 char *p;
1060 char *a = strSkipSpaces(currLine);
1061 memset(res, 0, sizeof(res));
1062 if (!a[0]) fatal("identifier expected");
1063 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1064 for (; p > res && isSpace(p[-1]); --p) {}
1065 *p = '\0';
1066 if (p-res > 120) fatal("identifier too long: %s", res);
1067 while (*a && isSpace(*a)) ++a;
1068 if (*a) {
1069 memmove(currLine, a, strlen(a)+1);
1070 if (currLine[0] == ';') currLine[0] = 0;
1071 if (currLine[0]) fatal("extra arguments");
1072 } else {
1073 currLine[0] = '\0';
1075 for (char *t = res; *t; ++t) *t = toLower(*t);
1076 return res;
1080 /* get label argument */
1081 static char *getLabelArg (int checkdelim) {
1082 static char res[MAX_LINE_SIZE+128];
1083 char *p;
1084 char *a = strSkipSpaces(currLine);
1085 memset(res, 0, sizeof(res));
1086 if (!a[0]) fatal("label expected");
1087 for (p = res; *a && *a != ',' && *a != ':' && *a != '=' && !isSpace(*a); ++a, ++p) *p = *a;
1088 for (; p > res && isSpace(p[-1]); --p) {}
1089 *p = '\0';
1090 if (p-res > 120) fatal("label name too long: %s", res);
1091 while (*a && isSpace(*a)) ++a;
1092 if (*a) {
1093 if (checkdelim && a[0] != ',' && a[0] != ':') fatal("bad label expression");
1094 memmove(currLine, a, strlen(a)+1);
1095 } else {
1096 currLine[0] = '\0';
1098 return res;
1102 /* get label argument, and ensure that it is the last one */
1103 static char *getOneLabelArg (void) {
1104 char *res = getLabelArg(1);
1105 if (currLine[0] && currLine[0] != ':') fatal("too many expressions");
1106 return res;
1110 /* returns ',' or 0 */
1111 static char eatComma (void) {
1112 char *a = strSkipSpaces(currLine);
1113 if (!a[0]) { currLine[0] = '\0'; return 0; }
1114 if (a[0] == ':') return 0;
1115 if (a[0] != ',') fatal("invalid expression: ',' expected");
1116 for (++a; *a && isSpace(*a); ++a) {}
1117 if (!a[0]) { currLine[0] = '\0'; return 0; }
1118 memmove(currLine, a, strlen(a)+1);
1119 return ',';
1123 ///////////////////////////////////////////////////////////////////////////////
1124 // label processor
1126 static MAYBE_UNUSED void removeSpacesAndColons (void) {
1127 char *ep = strSkipSpacesColons(currLine);
1128 memmove(currLine, ep, strlen(ep)+1);
1132 static void checkExprEnd (void) {
1133 char *ep = strSkipSpaces(currLine);
1134 memmove(currLine, ep, strlen(ep)+1);
1135 if (currLine[0] && currLine[0] != ':') fatal("end of expression expected");
1139 static void checkOperatorEnd (void) {
1140 char *ep = strSkipSpaces(currLine);
1141 memmove(currLine, ep, strlen(ep)+1);
1142 if (currLine[0]) fatal("end of operator expected");
1146 /* remove label from curLine */
1147 static void removeLabel (void) {
1148 char *ep = currLine;
1149 if (ep[0] && !isSpace(ep[0])) for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {} // skip text
1150 // skip spaces and colons
1151 ep = strSkipSpacesColons(ep);
1152 memmove(currLine, ep, strlen(ep)+1);
1156 static int labelDoEQU (const char *lblname, const char *value) {
1157 static char n2[256];
1158 UrLabelInfo *lbl;
1160 if (value == NULL || lblname == NULL || !lblname[0] || strlen(lblname) > 255 || strlen(value) >= MAX_LINE_SIZE) return -1;
1161 memset(n2, 0, sizeof(n2));
1162 strcpy(n2, lblname);
1163 if (!urasm_is_valid_name(n2)) return -1; // this is not a valid label, get out of here
1164 // check if this can be an instruction
1165 lbl = urAddLabel(lblname);
1166 if (!lbl->refFile) {
1167 lbl->refLine = 0;
1168 lbl->refFile = strdup("artificially-defined-label");
1171 strcpy(currLine, value);
1174 int defined = 1, addr = UR_FIXUP_NONE;
1175 int32_t res = getOneExprArg(&defined, &addr);
1177 lbl->type = 1; // equ label
1178 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1179 if (defined) {
1180 lbl->value = res;
1181 lbl->known = 1;
1182 } else {
1183 return -1; //fatal("can't calculate label %s", lbl->name);
1186 return 0;
1190 static void processLabel (void) {
1191 char *argstart;
1192 char *ep, *ln, *nn;
1193 static char n2[256];
1194 UrLabelInfo *lbl;
1195 int noLocAff = 0, doEQU = 0;
1196 /*!fprintf(stderr, "LINE <%s> (%d)\n", curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
1197 memset(n2, 0, sizeof(n2));
1198 if (!currLine[0] || isSpace(currLine[0]) || currLine[0] == ':') {
1199 // this may be " id = smth" or " id equ smth"
1200 ep = currLine;
1201 // skip spaces
1202 while (isSpace(*ep)) ++ep;
1203 if (ep[0] == ':' || !ep[0] || (!isAlpha(ep[0]) && ep[0] != '_' && ep[0] != '.' && ep[0] != '@')) {
1204 removeLabel(); // removeLabel() removes any spaces, etc
1205 return;
1207 // this looks like a label; check for '=' or 'equ'
1208 while (isAlphaDigit(ep[0]) || ep[0] == '_' || ep[0] == '.' || ep[0] == '@') ++ep;
1209 nn = ep;
1210 // skip trailing spaces
1211 while (isSpace(*ep)) ++ep;
1212 if (ep[0] == '=') {
1213 doEQU = 0;
1214 argstart = ++ep;
1215 } else if (isSpace(*nn)) {
1216 doEQU = 1;
1217 argstart = strIsCommand("EQU", ep);
1218 } else {
1219 argstart = NULL;
1221 if (!argstart) {
1222 removeLabel(); // removeLabel() removes any spaces, etc
1223 return;
1225 // remove leading spaces from name
1226 // copy label
1227 ep = currLine;
1228 // skip spaces
1229 while (isSpace(*ep)) ++ep;
1230 if (ep >= nn) fatal("internal compiler error");
1231 if (nn-ep > 120) fatal("label too long");
1232 memset(n2, 0, sizeof(n2));
1233 memmove(n2, ep, nn-ep);
1234 if (urFindOp(n2) || !urasm_is_valid_name(n2)) {
1235 //fatal("invalid label name");
1236 removeLabel(); // removeLabel() removes any spaces, etc
1237 return;
1239 // remove label name
1240 memmove(currLine, argstart, strlen(argstart)+1);
1241 // append label
1242 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1243 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1244 lbl = urAddLabel(nn);
1245 if (!lbl->refFile) {
1246 lbl->refLine = curSrcLine->lineNo;
1247 lbl->refFile = strdup(curSrcLine->fname);
1249 if (doEQU && pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1250 int defined = 1, addr = UR_FIXUP_NONE;
1251 int32_t res = getOneExprArg(&defined, &addr);
1252 lbl->type = doEQU;
1253 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1254 if (defined) {
1255 lbl->value = res;
1256 lbl->known = 1;
1257 } else {
1258 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1260 currLine[0] = '\0';
1261 return;
1263 // collect label
1264 for (ep = currLine; *ep && !isSpace(*ep) && *ep != ':'; ++ep) {}
1265 if (ep-currLine > 120) fatal("label too long");
1266 // copy label
1267 memset(n2, 0, sizeof(n2));
1268 memmove(n2, currLine, ep-currLine);
1269 if (urFindOp(n2)) {
1270 ep = strSkipSpaces(ep);
1271 if (*ep != ':') return; // this must be an instruction, process it
1273 if (!urasm_is_valid_name(n2)) return; // this is not a valid label, get out of here
1274 // check for macro
1275 if (findMacro(n2)) { /*fprintf(stderr, "***M:<%s>\n", n2);*/ return; } // macro call
1276 // check if this can be instruction
1277 //ep = strSkipSpaces(ep);
1278 //if (*ep != ':' && urFindOp(n2)) return; // this must be and instruction, process it
1279 // ok, we got a good label
1280 removeLabel();
1281 if (n2[0] == '@' && n2[1]) { noLocAff = 1; memmove(n2, n2+1, strlen(n2)); }
1282 /*!fprintf(stderr, "GOT LABEL <%s> (%d)\n", n2, (curSrcLine ? curSrcLine->lineNo : 0));*/
1283 nn = fixGlobalLabel((ln = fixLocalLabel(n2)));
1284 /*!fprintf(stderr, " RENAMED(ln) <%s>\n", ln);*/
1285 /*!fprintf(stderr, " RENAMED(nn) <%s>\n", nn);*/
1286 lbl = urAddLabel(nn);
1287 if (!lbl->refFile) {
1288 lbl->refLine = curSrcLine->lineNo;
1289 lbl->refFile = strdup(curSrcLine->fname);
1291 //printf("new: [%s]\n", lbl->name);
1292 // get command name
1293 if (currLine[0] == '=') {
1294 doEQU = 0;
1295 argstart = currLine+1;
1296 } else {
1297 doEQU = 1;
1298 argstart = strIsCommand("EQU", currLine);
1300 if (!argstart || doEQU) {
1301 if (pass == 0 && lbl->type != -1) fatal("duplicate label '%s'", lbl->name);
1303 if (argstart) {
1304 // do '=' or 'EQU'
1305 memmove(currLine, argstart, strlen(argstart)+1);
1306 if (!doEQU) {
1307 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label '%s'", lbl->name);
1309 int defined = 1, addr = UR_FIXUP_NONE;
1310 int32_t res = getOneExprArg(&defined, &addr);
1311 lbl->type = doEQU;
1312 if (addr != UR_FIXUP_NONE) lbl->fixuptype = addr;
1313 if (defined) {
1314 lbl->value = res;
1315 lbl->known = 1;
1316 } else {
1317 if (pass != 0) fatal("can't calculate label '%s'", lbl->name);
1319 currLine[0] = '\0';
1320 return;
1322 // code label
1323 if (lbl->name[0] != '{' && !noLocAff) {
1324 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
1325 lastSeenGlobalLabel = strdup(lbl->name);
1327 lbl->type = 2;
1328 lbl->value = disp;
1329 lbl->known = 1;
1330 lbl->fixuptype = UR_FIXUP_WORD;
1334 ///////////////////////////////////////////////////////////////////////////////
1335 // instruction finder (in source)
1337 /* array ends with NULL */
1338 /* returns line or NULL */
1339 /* iidx will be set to found instruction number */
1340 static __attribute__((sentinel)) SourceLine *findNextInstructionFromList (int *iidx, ...) {
1341 if (iidx) *iidx = -1;
1342 for (SourceLine *cur = curSrcLine; cur; cur = cur->next) {
1343 va_list ap;
1344 //if (!isSpace(cur->line[0])) continue; // fuck labeled strings
1345 va_start(ap, iidx);
1346 for (int f = 0; ;++f) {
1347 const char *name = va_arg(ap, const char *);
1349 if (!name) break;
1350 if (strIsCommand(name, cur->line)) {
1351 va_end(ap);
1352 if (iidx) *iidx = f;
1353 return cur;
1356 va_end(ap);
1358 return NULL;
1362 static MAYBE_UNUSED SourceLine *findNextInstruction (const char *name) {
1363 return findNextInstructionFromList(NULL, name, NULL);
1367 ///////////////////////////////////////////////////////////////////////////////
1368 // writers
1370 static int optWriteFixups = 0;
1371 static int optFixupType = 0; // 0: text file; 1: zas file; 2: difzas; 3: zas with zero endmarker
1372 static int optRunTape = 1;
1373 static int optRunDMB = 1;
1374 static int optRunSCL = 1;
1375 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
1376 static int optSNA48 = 1;
1377 static char *optOutputDir = NULL;
1380 ///////////////////////////////////////////////////////////////////////////////
1381 static void writeFixups (void) {
1383 void writeFixupList (FILE *fo, int cnt, const char *lbl, int (*chk)(const FixupItem *)) {
1384 if (cnt > 0) {
1385 int prevaddr = 0;
1386 fprintf(fo, "%s:\n", lbl);
1387 if (optFixupType == 1) fprintf(fo, " dw %d ; count\n", cnt);
1388 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1389 if (chk(fx)) {
1390 fprintf(fo, " dw #%04X\n", fx->opaddr-prevaddr);
1391 if (optFixupType == 2) {
1392 prevaddr = fx->opaddr;
1396 if (optFixupType == 2 || optFixupType == 3) fprintf(fo, " dw 0 ; end marker\n");
1400 if (optFixupType == 0) {
1401 /* simple text file */
1402 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.txt");
1403 printf("writing fixups to '%s'...\n", fname);
1404 FILE *fo = fopen(fname, "w");
1405 free(fname);
1406 if (fo == NULL) fatal("can't write fixup file");
1407 fprintf(fo, "; addr dadr sz\n");
1408 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1409 static const char type[4] = "NWLH";
1410 fprintf(fo, "%c #%04x #%04x %d\n", type[fx->fixuptype], fx->opaddr, fx->opdestaddr, fx->size);
1412 fclose(fo);
1413 } else {
1414 /* various asm formats */
1415 char *fname = strprintf("%s/%s", optOutputDir, "zfixuptable.zas");
1416 printf("writing fixups to '%s'...\n", fname);
1417 FILE *fo = fopen(fname, "w");
1418 int cntw = 0, cntwl = 0, cntwh = 0, cntbl = 0, cntbh = 0;
1419 free(fname);
1420 if (fo == NULL) fatal("can't write fixup file");
1421 for (const FixupItem *fx = fixlisthead; fx != NULL; fx = fx->next) {
1422 if (fx->fixuptype == UR_FIXUP_WORD) { ++cntw; continue; }
1423 switch (fx->fixuptype) {
1424 case UR_FIXUP_LOBYTE: if (fx->size == 2) ++cntwl; else ++cntbl; break;
1425 case UR_FIXUP_HIBYTE: if (fx->size == 2) ++cntwh; else ++cntbh; break;
1428 writeFixupList(fo, cntw, "fixup_table_w", lambda(int, (const FixupItem *fx) {
1429 return (fx->fixuptype == UR_FIXUP_WORD);
1430 }));
1431 writeFixupList(fo, cntwl, "fixup_table_wl", lambda(int, (const FixupItem *fx) {
1432 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 2);
1433 }));
1434 writeFixupList(fo, cntwh, "fixup_table_wh", lambda(int, (const FixupItem *fx) {
1435 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 2);
1436 }));
1437 writeFixupList(fo, cntbl, "fixup_table_bl", lambda(int, (const FixupItem *fx) {
1438 return (fx->fixuptype == UR_FIXUP_LOBYTE && fx->size == 1);
1439 }));
1440 writeFixupList(fo, cntbh, "fixup_table_bh", lambda(int, (const FixupItem *fx) {
1441 return (fx->fixuptype == UR_FIXUP_HIBYTE && fx->size == 1);
1442 }));
1443 fclose(fo);
1448 ///////////////////////////////////////////////////////////////////////////////
1449 /* return 'found' flag */
1450 static int findChunkFrom (int addr, int *start, int *len) {
1451 if (addr < 0) addr = 0;
1452 for (; addr <= 65535 && !memused[addr]; ++addr) {}
1453 if (addr > 65535) return 0;
1454 *start = addr;
1455 for (; addr <= 65535 && memused[addr]; ++addr) {}
1456 *len = addr-(*start);
1457 return 1;
1461 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
1462 for (; buflen >= 2; buflen -= 2) {
1463 int cnt = *buf++;
1464 uint8_t byte = *buf++;
1466 if (cnt == 0) {
1467 // copy
1468 for (cnt = byte; cnt >= 0; --cnt, ++addr, --buflen, ++buf) {
1469 if (!memused[addr]) putByte(addr, *buf);
1470 if (addr == 0xffff) return;
1472 } else {
1473 // fill
1474 for (; cnt > 0; --cnt, ++addr) {
1475 if (!memused[addr]) putByte(addr, byte);
1476 if (addr == 0xffff) return;
1483 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1484 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1485 if (bxor) *bxor = (*bxor)^b;
1486 return 0;
1490 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1491 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1492 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1493 return 0;
1497 ///////////////////////////////////////////////////////////////////////////////
1498 // .sna
1500 static int saveSna (const char *fname, int as48) {
1501 char *fn;// = malloc(strlen(fname)+16);
1502 uint8_t regs[27];
1503 FILE *fo;
1504 char abuf[32];
1506 fn = strprintf("%s/%s.sna", optOutputDir, fname);
1507 fo = fopen(fn, "wb");
1508 free(fn);
1509 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
1510 printf("out: %s.sna\n", fname);
1511 memmove(regs, ursna48, 27);
1512 #if 0
1513 if (optRunSNA) {
1514 /* push new address */
1515 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1516 sp -= 2;
1517 putByte(sp, ent&0xFFU);
1518 putByte(sp+1, (ent>>8)&0xFFU);
1519 regs[23] = sp&0xFFU;
1520 regs[24] = (sp>>8)&0xFFU;
1522 fwrite(regs, 27, 1, fo);
1523 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
1524 fwrite(memory+16384, 49152, 1, fo);
1525 #endif
1527 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
1529 //fprintf(stderr, "SP: #%04X #%02X%02X\n", sp, memory[sp+1], memory[sp]);
1530 //fprintf(stderr, "%d\n", memused[sp]);
1531 if (memused[sp] || memused[(sp+1)&0xffff]) {
1532 fprintf(stderr, "FATAL: can't save snapshot!\n");
1533 goto error;
1536 if (fwrite(ursna48, 27, 1, fo) != 1) goto error;
1537 rleUnpack(16384, (const uint8_t *)ursna48+27, sizeof(ursna48)-27);
1538 //for (int f = 0; f < 49152; ++f) if (!memused[f+16384]) memory[f+16384] = ursna48[f+27];
1540 sprintf(abuf, "%05d", clrAddr);
1541 memcpy(memory+23762, abuf, 5);
1542 sprintf(abuf, "%05d", ent);
1543 memcpy(memory+23773, abuf, 5);
1545 if (fwrite(memory+16384, 49152, 1, fo) != 1) goto error;
1547 if (!as48) {
1548 int addr = memory[sp]+256*memory[(sp+1)&0xffff];
1550 if (fWriteWord(fo, addr, NULL) != 0) goto error; // PC
1551 if (fWriteByte(fo, 0x10, NULL) != 0) goto error; // 7FFD
1552 if (fWriteByte(fo, 0, NULL) != 0) goto error; // TR-DOS inactive
1553 // write pages
1554 memset(memory, 0, 16384);
1555 for (int f = 1; f < 8; ++f) {
1556 if (f != 2 && f != 5) {
1557 if (fwrite(memory, 16384, 1, fo) != 1) goto error;
1562 fclose(fo);
1563 return 0;
1564 error:
1565 fclose(fo);
1566 unlink(fname);
1567 return -1;
1571 ///////////////////////////////////////////////////////////////////////////////
1572 // bin chunks
1574 static void saveRaw (const char *basename) {
1575 char *fname = NULL;// = malloc(strlen(basename)+16);
1576 int start = 0, len;
1577 FILE *fo;
1579 while (findChunkFrom(start, &start, &len)) {
1580 if (fname != NULL) free(fname);
1581 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, (optTapExt ? "tap" : "bin"));
1582 fo = fopen(fname, "wb");
1583 if (!fo) {
1584 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1585 } else {
1586 printf("out: %s\n", fname);
1587 if (fwrite(memory+start, len, 1, fo) != 1) {
1588 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1589 fclose(fo);
1590 unlink(fname);
1591 } else {
1592 fclose(fo);
1595 start += len;
1597 if (fname != NULL) free(fname);
1601 ///////////////////////////////////////////////////////////////////////////////
1602 // HoBeta files
1604 typedef struct __attribute__((packed)) __attribute__((gcc_struct)) {
1605 char name[8];
1606 char type;
1607 uint16_t start;
1608 uint16_t len;
1609 uint8_t zero; // always zero
1610 uint8_t secLen; // length in sectors
1611 uint16_t checksum;
1612 } HOBHeader;
1615 static uint16_t calcHobSum (const void *hdr) {
1616 const uint8_t *buf = (const uint8_t *)hdr;
1617 uint16_t res = 0;
1619 for (unsigned int f = 0; f < 15; ++f) res += ((uint16_t)buf[f])*257+f;
1620 return res;
1624 static void saveHob (const char *basename) {
1625 char *fname = NULL;//malloc(strlen(basename)+16);
1626 int start = 0, len;
1627 HOBHeader hdr;
1628 FILE *fo;
1630 while (findChunkFrom(start, &start, &len)) {
1631 if (fname != NULL) free(fname);
1632 fname = strprintf("%s/%s_%04X.%s", optOutputDir, basename, start, "$C");
1633 fo = fopen(fname, "wb");
1634 if (!fo) {
1635 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1636 start += len;
1637 } else {
1638 char tmpbuf[sizeof(hdr.name)*2];
1639 printf("out: %s\n", fname);
1640 memset(&hdr, 0, sizeof(hdr));
1641 memset(&hdr.name, 32, 8);
1642 snprintf(tmpbuf, sizeof(tmpbuf), "%c%04X", basename[0], start);
1643 while (strlen(tmpbuf) < sizeof(hdr.name)) strcat(tmpbuf, " "); /* sorry! */
1644 memcpy(hdr.name, tmpbuf, sizeof(hdr.name));
1645 hdr.type = 'C';
1646 hdr.start = start;
1647 hdr.len = len;
1648 hdr.secLen = (len+255)/256;
1649 hdr.checksum = calcHobSum(&hdr);
1650 if (fwrite(&hdr, sizeof(hdr), 1, fo) != 1) {
1651 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1652 fclose(fo);
1653 unlink(fname);
1654 goto quit;
1656 if (fwrite(memory+start, len, 1, fo) != 1) {
1657 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1658 fclose(fo);
1659 unlink(fname);
1660 goto quit;
1662 start += len;
1663 while (len%256) {
1664 if (fwrite(&hdr.zero, 1, 1, fo) != 1) {
1665 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1666 fclose(fo);
1667 unlink(fname);
1668 goto quit;
1670 ++len;
1671 //fprintf(stderr, ":%d\n", len%256);
1673 fclose(fo);
1676 quit:
1677 if (fname != NULL) free(fname);
1681 // ////////////////////////////////////////////////////////////////////////// //
1682 typedef struct {
1683 uint8_t fcount;
1684 uint8_t dir[14*256];
1685 uint32_t dirpos;
1686 uint32_t fpos;
1687 uint8_t *data;
1688 size_t datapos;
1689 size_t datasize;
1690 } SCLFile;
1693 static void sclInit (SCLFile *scl) {
1694 memset(scl, 0, sizeof(*scl));
1695 scl->datasize = 2*80*16*256; /* maximum disk size */
1696 scl->data = malloc(scl->datasize);
1697 memset(scl->data, 0, scl->datasize);
1698 scl->fpos = 0xffffffffu;
1702 static void sclFree (SCLFile *scl) {
1703 if (!scl) return;
1704 if (scl->data) free(scl->data);
1705 memset(scl, 0, sizeof(*scl));
1706 scl->fpos = 0xffffffffu;
1710 static void sclStartFile (SCLFile *scl, const char *name, char type, uint16_t v0, uint16_t v1) {
1711 if (scl->fcount == 255) {
1712 fprintf(stderr, "FATAL: too many files in SCL!\n");
1713 exit(1);
1715 if (scl->fpos != 0xffffffffu) {
1716 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1717 exit(1);
1719 int nlen = 0;
1720 while (nlen < 8 && name && name[nlen]) scl->dir[scl->dirpos++] = name[nlen++];
1721 while (nlen < 8) { scl->dir[scl->dirpos++] = ' '; ++nlen; }
1722 scl->dir[scl->dirpos++] = type;
1723 scl->dir[scl->dirpos++] = v0&0xff;
1724 scl->dir[scl->dirpos++] = (v0>>8)&0xff;
1725 scl->dir[scl->dirpos++] = v1&0xff;
1726 scl->dir[scl->dirpos++] = (v1>>8)&0xff;
1727 //scl->dir[scl->dirpos++] = 0; /* size in sectors, unknown yet */
1728 scl->fpos = scl->datapos;
1732 static void sclEndFile (SCLFile *scl) {
1733 if (scl->fpos == 0xffffffffu) {
1734 fprintf(stderr, "FATAL: last SCL file already finished!\n");
1735 exit(1);
1737 while (scl->datapos%256) scl->data[scl->datapos++] = 0;
1738 const uint32_t fsz = scl->datapos-scl->fpos;
1739 if (fsz > 255*256) {
1740 fprintf(stderr, "FATAL: SCL file too big!\n");
1741 exit(1);
1743 scl->dir[scl->dirpos++] = ((fsz+255)/256)&0xff; /* size in sectors */
1744 ++scl->fcount;
1745 scl->fpos = 0xffffffffu;
1749 static void sclWriteData (SCLFile *scl, const void *buf, size_t len) {
1750 if (scl->fpos == 0xffffffffu) {
1751 fprintf(stderr, "FATAL: no open SCL file!\n");
1752 exit(1);
1754 if (!len) return;
1755 if (len > 255*256) {
1756 fprintf(stderr, "FATAL: SCL file too big!\n");
1757 exit(1);
1759 memcpy(scl->data+scl->datapos, buf, len);
1760 scl->datapos += len;
1764 static void sclWriteByte (SCLFile *scl, uint8_t b) {
1765 sclWriteData(scl, &b, 1);
1769 static void sclWriteWord (SCLFile *scl, uint16_t w) {
1770 uint8_t b = w&0xff;
1771 sclWriteData(scl, &b, 1);
1772 b = (w>>8)&0xff;
1773 sclWriteData(scl, &b, 1);
1777 #define SCL_DO_WRITE(buf_,size_) do { \
1778 if ((size_) == 0) break; \
1779 const uint8_t *p = (const uint8_t *)(buf_); \
1780 for (size_t n = (size_t)(size_); n--; ++p) checksum += *p; \
1781 if (fwrite((buf_), (size_t)(size_), 1, fo) != 1) return -1; \
1782 } while (0)
1784 // <0: error; 0: ok
1785 static int sclSaveToFile (FILE *fo, SCLFile *scl) {
1786 if (scl->fpos != 0xffffffffu) {
1787 fprintf(stderr, "FATAL: last SCL file is not finished!\n");
1788 exit(1);
1790 const char *sign = "SINCLAIR";
1791 uint32_t checksum = 0;
1792 // header
1793 SCL_DO_WRITE(sign, 8);
1794 SCL_DO_WRITE(&scl->fcount, 1);
1795 // directory
1796 SCL_DO_WRITE(scl->dir, scl->dirpos);
1797 // data
1798 SCL_DO_WRITE(scl->data, scl->datapos);
1799 // write checksum
1800 for (unsigned f = 0; f < 4; ++f) {
1801 const uint8_t b = (checksum>>(f*8))&0xff;
1802 SCL_DO_WRITE(&b, 1);
1804 // done
1805 return 0;
1809 // ////////////////////////////////////////////////////////////////////////// //
1810 static void saveSCLCargador (SCLFile *scl) {
1811 static uint8_t cargador[16384]; // should be enough for everyone
1812 int linestart = -1;
1813 int linenum = 0;
1814 int start = 0, len, pos;
1816 void putStr (const char *s) {
1817 for (; *s; ++s) cargador[pos++] = *s;
1820 void putNum (int num) {
1821 char buf[64];
1822 sprintf(buf, "%d", num);
1823 putStr(buf);
1826 void startLine () {
1827 ++linenum;
1828 linestart = pos;
1829 // number
1830 cargador[pos++] = (linenum>>8)&0xff;
1831 cargador[pos++] = linenum&0xff;
1832 // size (will be fixed later)
1833 cargador[pos++] = 0;
1834 cargador[pos++] = 0;
1837 void flushLine () {
1838 if (linestart >= 0) {
1839 const int size = pos-linestart-4;
1840 cargador[linestart+2] = size&0xff;
1841 cargador[linestart+3] = (size>>8)&0xff;
1843 linestart = -1;
1846 char cname[16];
1848 pos = 0;
1849 startLine();
1850 // CLEAR VAL "xxx"
1851 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\"");
1852 // load blocks
1853 int cont = 1;
1854 while (findChunkFrom(start, &start, &len)) {
1855 // :RANDOMIZE USR VAL "15619":REM:LOAD "
1856 if (cont) { putStr(":"); cont = 0; }
1857 putStr("\xf9\xc0\xb0\""); putNum(15619); putStr("\":\xea:\xef\"");
1858 // generate chunk name
1859 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
1860 putStr(cname);
1861 //" CODE
1862 putStr("\"\xaf\r");
1863 flushLine();
1864 startLine();
1865 start += len;
1867 // and run
1868 if (cont) { putStr(":"); cont = 0; }
1869 // RANDOMIZE USR VAL "xxx"
1870 putStr("\xf9\xc0\xb0\""); putNum(ent); putStr("\"");
1871 putStr("\r");
1872 flushLine();
1874 //putWord(0xaa80);
1875 //putWord(1); // autostart line
1877 // write to SCL
1878 sclStartFile(scl, "CARGADOR", 'B', (unsigned)pos, (unsigned)pos);
1879 sclWriteData(scl, cargador, (size_t)pos);
1880 sclWriteByte(scl, 0x80);
1881 sclWriteByte(scl, 0xaa);
1882 sclWriteWord(scl, 1);
1883 sclEndFile(scl);
1887 static void saveSCL (const char *basename) {
1888 SCLFile scl;
1889 int fcount = 0;
1890 int start, len;
1891 char *fname = strprintf("%s/%s.scl", optOutputDir, basename);
1892 // count files
1893 start = 0;
1894 while (findChunkFrom(start, &start, &len)) {
1895 ++fcount;
1896 start += len;
1898 if (fcount && optRunSCL) fcount += 2; // +loader and boot
1899 if (fcount > 255) {
1900 fprintf(stderr, "ERROR: can't write file '%s' (too many chunks: %d)!\n", fname, fcount);
1901 exit(1);
1903 // create output file
1904 FILE *fo = fopen(fname, "wb");
1905 if (!fo) {
1906 fprintf(stderr, "ERROR: can't write file '%s'!\n", fname);
1907 exit(1);
1909 // initialise SCL writer
1910 sclInit(&scl);
1911 // write loader
1912 if (fcount && optRunSCL) {
1913 // create simple boot
1914 const uint8_t dasboot[] = {
1915 0xf9,0xc0,0xb0,'"','1','5','6','1','9','"',':',0xea,':',0xef,'"','C','A','R','G','A','D','O','R','"','\r', //RANDOMIZE USR VAL "15619":REM:LOAD "CARGADOR"
1917 sclStartFile(&scl, "boot", 'B', sizeof(dasboot)+4, sizeof(dasboot)+4);
1918 sclWriteWord(&scl, 1); // line number
1919 sclWriteWord(&scl, (uint16_t)sizeof(dasboot)); // line size
1920 sclWriteData(&scl, dasboot, sizeof(dasboot)); // line data
1921 // TR-DOS signature
1922 sclWriteByte(&scl, 0x80);
1923 sclWriteByte(&scl, 0xaa);
1924 // start line
1925 sclWriteWord(&scl, 0);
1926 sclEndFile(&scl);
1927 saveSCLCargador(&scl);
1929 // write chunks
1930 char cname[16];
1931 start = 0;
1932 while (findChunkFrom(start, &start, &len)) {
1933 snprintf(cname, sizeof(cname), "c%04X", (unsigned)start);
1934 sclStartFile(&scl, cname, 'C', (unsigned)start, (unsigned)len);
1935 sclWriteData(&scl, memory+(unsigned)start, (unsigned)len);
1936 sclEndFile(&scl);
1937 start += len;
1939 if (sclSaveToFile(fo, &scl) < 0) {
1940 fprintf(stderr, "ERROR: error writing file '%s'!\n", fname);
1941 fclose(fo);
1942 unlink(fname);
1943 exit(1);
1945 sclFree(&scl);
1946 fclose(fo);
1947 printf("out: %s\n", fname);
1948 if (fname != NULL) free(fname);
1952 ///////////////////////////////////////////////////////////////////////////////
1953 // .dmb
1955 static int saveDMB (const char *fname) {
1956 char *fn;// = malloc(strlen(fname)+16);
1957 int start = 0, len;
1958 uint16_t pcnt = 0;
1959 FILE *fo;
1961 fn = strprintf("%s/%s.dmb", optOutputDir, fname);
1962 fo = fopen(fn, "wb");
1963 free(fn);
1964 if (!fo) { fprintf(stderr, "ERROR: can't write DMB file: %s.dmb\n", fname); return -1; }
1966 while (findChunkFrom(start, &start, &len)) { ++pcnt; start += len; }
1968 printf("out: %s.dmb\n", fname);
1969 if (fwrite("DMB1", 4, 1, fo) != 1) goto error;
1970 if (fWriteWord(fo, (optRunDMB ? ent : 0), NULL) != 0) goto error;
1971 if (fWriteWord(fo, clrAddr, NULL) != 0) goto error;
1972 if (fWriteWord(fo, pcnt, NULL) != 0) goto error;
1974 start = 0;
1975 while (findChunkFrom(start, &start, &len)) {
1976 if (fWriteWord(fo, len, NULL) != 0) goto error;
1977 if (fWriteWord(fo, start, NULL) != 0) goto error;
1978 if (fwrite(memory+start, len, 1, fo) != 1) goto error;
1979 start += len;
1981 fclose(fo);
1982 return 0;
1983 error:
1984 fclose(fo);
1985 unlink(fname);
1986 fprintf(stderr, "ERROR: error writing file %s.dmb!\n", fname);
1987 return -1;
1991 ///////////////////////////////////////////////////////////////////////////////
1992 // .tap
1994 static char tapeLoaderName[16];
1997 static void saveTapCargador (FILE *fo) {
1998 // count blocks
1999 static uint8_t cargador[16384]; // should be enough for everyone
2000 int start = 0, len, pos, f;
2001 uint8_t bxor;
2004 void putStr (const char *s) {
2005 for (; *s; ++s) cargador[pos++] = *s;
2008 void putNum (int num) {
2009 char buf[64];
2010 sprintf(buf, "%d", num);
2011 putStr(buf);
2015 pos = 4;
2016 // number
2017 cargador[0] = 0; cargador[1] = 10;
2018 // size (will be fixed later)
2019 putStr("\xfd\xb0\""); putNum(clrAddr); putStr("\""); // CLEAR VAL "xxx"
2020 // load blocks
2021 while (findChunkFrom(start, &start, &len)) {
2022 // :LOAD "" CODE
2023 putStr(":\xef\"\"\xaf");
2024 start += len;
2026 // and run
2027 // :RANDOMIZE USR VAL "xxx"
2028 putStr(":\xf9\xc0\xb0\""); putNum(ent); putStr("\"\r");
2029 // patch len
2030 cargador[2] = (pos-4)&0xff;
2031 cargador[3] = ((pos-4)>>8)&0xff;
2032 // write header
2033 fWriteWord(fo, 19, NULL); // length of header
2034 bxor = 0;
2035 fWriteByte(fo, 0, &bxor); // header block
2036 fWriteByte(fo, 0, &bxor); // 'basic' flag
2037 if (tapeLoaderName[0]) {
2038 for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2039 } else {
2040 fWriteByte(fo, 'c', &bxor);
2041 fWriteByte(fo, 'a', &bxor);
2042 fWriteByte(fo, 'r', &bxor);
2043 fWriteByte(fo, 'g', &bxor);
2044 fWriteByte(fo, 'a', &bxor);
2045 fWriteByte(fo, 'd', &bxor);
2046 fWriteByte(fo, 'o', &bxor);
2047 fWriteByte(fo, 'r', &bxor);
2048 fWriteByte(fo, ' ', &bxor);
2049 fWriteByte(fo, ' ', &bxor);
2051 fWriteWord(fo, pos, &bxor); // length
2052 fWriteWord(fo, 10, &bxor); // start
2053 fWriteWord(fo, pos, &bxor); // length2
2054 fWriteByte(fo, bxor, NULL);
2055 // write data
2056 fWriteWord(fo, pos+2, NULL); // plus type and checkbyte
2057 bxor = 0;
2058 fWriteByte(fo, 0xFFU, &bxor); // data block
2059 for (f = 0; f < pos; ++f) fWriteByte(fo, cargador[f], &bxor);
2060 fWriteByte(fo, bxor, NULL);
2064 static void saveTap (const char *basename) {
2065 char *fname;// = malloc(strlen(basename)+16);
2066 char blkname[128];
2067 int start = 0, len, f;
2068 uint8_t bxor;
2069 FILE *fo;
2071 fname = strprintf("%s/%s.tap", optOutputDir, basename);
2072 fo = fopen(fname, "wb");
2073 free(fname);
2074 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
2075 printf("out: %s.tap\n", basename);
2076 if (optRunTape) saveTapCargador(fo);
2077 while (findChunkFrom(start, &start, &len)) {
2078 // write header
2079 if (tapeLoaderName[0]) {
2080 //for (int f = 0; f < 10; ++f) fWriteByte(fo, tapeLoaderName[f], &bxor);
2081 memcpy(blkname, tapeLoaderName, 10);
2082 } else {
2083 sprintf(blkname, "c%04X:%04X", start, len);
2085 //printf(" block: %s\n", blkname);
2086 fWriteWord(fo, 19, NULL); // length of header
2087 bxor = 0;
2088 fWriteByte(fo, 0, &bxor); // header block
2089 fWriteByte(fo, 3, &bxor); // 'code' flag
2090 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
2091 fWriteWord(fo, len, &bxor);
2092 fWriteWord(fo, start, &bxor);
2093 fWriteWord(fo, 32768, &bxor);
2094 fWriteByte(fo, bxor, NULL);
2095 // write data
2096 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
2097 bxor = 0;
2098 fWriteByte(fo, 0xFFU, &bxor); // data block
2099 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
2100 fWriteByte(fo, bxor, NULL);
2101 start += len;
2103 fclose(fo);
2107 ///////////////////////////////////////////////////////////////////////////////
2108 // pseudoinstructions
2110 // note that processCurrentLine() will NOT skip to the next line before
2111 // calling pseudoinstruction handler!
2112 // note that processCurrentLine() will skip current line after calling
2113 // pseudoinstruction handler!
2115 static int wasOrg = 0;
2116 static int wasClr = 0;
2119 // ////////////////////////////////////////////////////////////////////////// //
2120 // print message using printf-like syntax
2121 // doesn't print new line
2122 static void processPrintf (FILE *fo, const char *fmt) {
2123 if (!fmt || !fmt[0]) return;
2124 /* it may be passed from argument parser, and destroyed by other arguments, so copy it */
2125 char *tempstr = NULL;
2126 size_t tempsize = 0;
2127 char *fmtcopy = malloc(strlen(fmt)+1);
2128 strcpy(fmtcopy, fmt);
2129 char *currfmt = fmtcopy;
2130 char tempbuf[512];
2131 int stlen;
2132 char *strarg;
2133 int defined;
2134 int32_t exprval;
2135 while (*currfmt) {
2136 /* find '%' */
2137 char *prcs = strchr(currfmt, '%');
2138 if (!prcs || !prcs[1]) {
2139 /* no more formatting; print the tail and exit the loop */
2140 fprintf(fo, "%s", currfmt);
2141 break;
2143 /* is this "%%"? */
2144 int docheck = 1;
2145 if (prcs[1] == '%') {
2146 ++prcs;
2147 docheck = 0;
2149 /* print up to `prcs` */
2150 if (prcs > currfmt) {
2151 size_t partlen = (ptrdiff_t)(prcs-currfmt);
2152 if (partlen+1 > tempsize) {
2153 tempsize = ((partlen+8)|0xff)+1;
2154 tempstr = realloc(tempstr, tempsize);
2156 memcpy(tempstr, currfmt, partlen);
2157 tempstr[partlen] = 0;
2158 fprintf(fo, "%s", tempstr);
2160 currfmt = ++prcs; /* skip percent */
2161 if (!docheck) continue;
2162 /* parse format */
2163 char sign = ' ';
2164 int width = 0;
2165 int zerofill = 0;
2166 char ftype = ' ';
2167 if (*currfmt == '+' || *currfmt == '-') sign = *currfmt++;
2168 if (sign != '-' && *currfmt == '0') zerofill = 1;
2169 while (isDigit(*currfmt)) { width = width*10+((*currfmt)-'0'); ++currfmt; }
2170 if (width > 256) width = 256;
2171 ftype = *currfmt++;
2172 if (!ftype) break; /* oops */
2173 if (!eatComma()) fatal("out of arguments for string format");
2174 switch (ftype) {
2175 case 's': /* string */
2176 stlen = 0;
2177 switch (strSkipSpaces(currLine)[0]) {
2178 case '"': case '\'': /* string literal? */
2179 strarg = getStrArg(&stlen);
2180 break;
2181 default: /* expression */
2182 strarg = getStrExprArg();
2183 stlen = (int)strlen(strarg);
2184 break;
2186 /* left pad */
2187 if (sign != '-' && stlen < width) {
2188 int padlen = width-stlen;
2189 memset(tempbuf, ' ', padlen);
2190 tempbuf[padlen+1] = 0;
2191 fprintf(fo, "%s", tempbuf);
2193 fprintf(fo, "%s", strarg);
2194 /* right pad */
2195 if (sign == '-' && stlen < width) {
2196 int padlen = width-stlen;
2197 memset(tempbuf, ' ', padlen);
2198 tempbuf[padlen+1] = 0;
2199 fprintf(fo, "%s", tempbuf);
2201 break;
2202 case 'd': /* decimal */
2203 defined = 1;
2204 exprval = getExprArg(&defined, NULL);
2205 if (width && zerofill) {
2206 fprintf(fo, "%0*d", width, exprval);
2207 } else if (width) {
2208 fprintf(fo, "%*d", (sign != '-' ? width : -width), exprval);
2209 } else {
2210 fprintf(fo, "%d", exprval);
2212 break;
2213 case 'c': /* char */
2214 defined = 1;
2215 exprval = getExprArg(&defined, NULL);
2216 if (exprval <= 0 || exprval == '?' || exprval > 255) exprval = '?';
2217 if (width) {
2218 fprintf(fo, "%*c", (sign != '-' ? width : -width), exprval);
2219 } else {
2220 fprintf(fo, "%c", exprval);
2222 break;
2223 case 'u': /* unsigned */
2224 defined = 1;
2225 exprval = getExprArg(&defined, NULL);
2226 if (width && zerofill) {
2227 fprintf(fo, "%0*u", width, (unsigned)(exprval&0xffff));
2228 } else if (width) {
2229 fprintf(fo, "%*u", (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2230 } else {
2231 fprintf(fo, "%u", (unsigned)(exprval&0xffff));
2233 break;
2234 case 'x': case 'X': /* hex */
2235 defined = 1;
2236 exprval = getExprArg(&defined, NULL);
2237 if (width && zerofill) {
2238 fprintf(fo, (ftype == 'x' ? "%0*x" : "%0*X"), width, (unsigned)(exprval&0xffff));
2239 } else if (width) {
2240 fprintf(fo, (ftype == 'x' ? "%*x" : "%*X"), (sign != '-' ? width : -width), (unsigned)(exprval&0xffff));
2241 } else {
2242 fprintf(fo, (ftype == 'x' ? "%x" : "%X"), (unsigned)(exprval&0xffff));
2244 break;
2245 default:
2246 if (ftype <= 0 || ftype == 127) ftype = '?';
2247 fatal("invalid format specifier: '%c'", ftype);
2248 break;
2251 if (tempstr) free(tempstr);
2252 free(fmtcopy);
2256 ///////////////////////////////////////////////////////////////////////////////
2257 // user error
2259 static int piERROR (void) {
2260 int len = 0;
2261 char *res = getStrArg(&len);
2262 fprintf(stdout, "*** USER ERROR: ");
2263 processPrintf(stdout, res);
2264 fputc('\n', stdout);
2265 fatal("user error abort");
2266 return PI_SKIP_LINE;
2270 static int piWARNING (void) {
2271 int len = 0;
2272 char *res = getStrArg(&len);
2273 fprintf(stdout, "*** USER WARNING ");
2274 if (curSrcLine) {
2275 fprintf(stdout, "at file %s, line %d: ", curSrcLine->fname, curSrcLine->lineNo);
2276 } else {
2277 fprintf(stdout, "somewhere in time: ");
2279 processPrintf(stdout, res);
2280 fputc('\n', stdout);
2281 return PI_SKIP_LINE;
2285 ///////////////////////////////////////////////////////////////////////////////
2286 // user warnings
2288 static int piPrintfCommon (int passNo) {
2289 if (passNo < 0 || pass == passNo) {
2290 int len = 0;
2291 char *res = getStrArg(&len);
2292 processPrintf(stdout, res);
2293 fputc('\n', stdout);
2295 return PI_SKIP_LINE;
2299 static int piDISPLAYX (int passNo, int asHex) {
2300 for (;;) {
2301 if (isStrArg()) {
2302 int len = 0;
2303 char *res = getStrArg(&len);
2305 if (passNo < 0 || pass == passNo) printf("%s", res);
2306 } else {
2307 int defined = 1;
2308 int32_t v = getExprArg(&defined, NULL);
2310 if (passNo < 0 || pass == passNo) {
2311 if (asHex) printf("%04X", (unsigned int)v);
2312 else printf("%d", v);
2315 if (!eatComma()) break;
2317 return PI_SKIP_LINE;
2321 static int piDISPLAY (void) { return piDISPLAYX(1, 0); }
2322 static int piDISPLAY0 (void) { return piDISPLAYX(0, 0); }
2323 static int piDISPLAYA (void) { return piDISPLAYX(-1, 0); }
2324 static int piDISPHEX (void) { return piDISPLAYX(1, 1); }
2325 static int piDISPHEX0 (void) { return piDISPLAYX(0, 1); }
2326 static int piDISPHEXA (void) { return piDISPLAYX(-1, 1); }
2328 static int piPRINTF (void) { return piPrintfCommon(1); } /* 2nd pass */
2329 static int piPRINTF0 (void) { return piPrintfCommon(0); } /* 1st pass */
2330 static int piPRINTFA (void) { return piPrintfCommon(-1); } /* all passes */
2333 ///////////////////////////////////////////////////////////////////////////////
2334 // ORG, DISP, etc.
2336 static int piORG (void) {
2337 int defined = 1;
2338 int32_t res = getOneExprArg(&defined, NULL);
2340 if (!defined) fatal("sorry, ORG operand value must be known here");
2341 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
2342 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
2343 pc = disp = res;
2344 if (!wasOrg) {
2345 wasOrg = 1;
2346 ent = res;
2347 if (!wasClr && res > 0) clrAddr = res-1;
2349 return PI_CONT_LINE;
2353 static int piDISP (void) {
2354 int defined = 1;
2355 int32_t res = getOneExprArg(&defined, NULL);
2357 if (!defined) fatal("sorry, DISP operand value must be known here");
2358 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
2359 //printf("DISP=%d\n", res);
2360 disp = res;
2361 return PI_CONT_LINE;
2365 static int piENDDISP (void) {
2366 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
2367 checkExprEnd();
2368 disp = pc;
2369 return PI_CONT_LINE;
2373 static int piENT (void) {
2374 int defined = 1;
2375 int32_t res = getOneExprArg(&defined, NULL);
2377 //if (!defined) fatal("sorry, ENT operand value must be known here");
2378 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
2379 ent = res;
2380 return PI_CONT_LINE;
2384 static int piCLR (void) {
2385 int defined = 1;
2386 int32_t res = getOneExprArg(&defined, NULL);
2388 //if (!defined) fatal("sorry, CLR operand value must be known here");
2389 if (res < 0 || res > 65535) fatal("invalid CLR operand value: %d", res);
2390 clrAddr = res;
2391 wasClr = 1;
2392 return PI_CONT_LINE;
2396 static int piRESERVE (void) {
2398 int defined = 1, start;
2399 int32_t res = getExprArg(&defined, NULL);
2400 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2401 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2402 start = res;
2403 if (!eatComma()) fatal("RESERVE needs 2 args");
2404 res = getOneExprArg(&defined, NULL);
2405 if (!defined) fatal("sorry, RESERVE operand values must be known here");
2406 if (res < 0 || res > 65535) fatal("invalid RESERVE operand value: %d", res);
2407 for (; len > 0; --len, start = (start+1)&0xffff) memresv[start] = 1;
2409 fatal("RESERVE: not yet!");
2410 return PI_CONT_LINE;
2414 static int piALIGN (void) {
2415 int defined = 1;
2416 int32_t res;
2418 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
2419 res = getOneExprArg(&defined, NULL);
2420 if (!defined) fatal("sorry, ALIGN operand value must be known here");
2421 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
2422 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
2423 if (res > 0 && pc%res != 0) {
2424 pc /= res;
2425 pc *= res;
2426 pc += res;
2427 disp = pc;
2429 return PI_CONT_LINE;
2433 static int piDISPALIGN (void) {
2434 int defined = 1;
2435 int32_t res = getOneExprArg(&defined, NULL);
2437 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
2438 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
2439 if (res > 0 && disp%res != 0) {
2440 disp /= res;
2441 disp *= res;
2442 disp += res;
2444 return PI_CONT_LINE;
2448 ///////////////////////////////////////////////////////////////////////////////
2449 // DEFx
2451 static int defIncr = 0;
2454 static int piDEFINCR (void) {
2455 int defined = 1;
2456 int32_t cnt = getOneExprArg(&defined, NULL);
2458 if (!defined) fatal("DEFINCR: increment must be defined");
2459 defIncr = cnt;
2460 return PI_CONT_LINE;
2464 static int piDEFBW (int isWord) {
2465 for (;;) {
2466 int defined = 0, fixuptype = UR_FIXUP_NONE;
2468 if (isStrArg()) {
2469 int f, len = 0;
2470 char *res = getStrArg(&len);
2472 for (f = 0; f < len; ++f) {
2473 int32_t b = (uint8_t)res[f];
2474 b += defIncr;
2475 if (b < -128 || b > 255) fatal("invalid operand value: %d", b);
2476 if (b < 0) b += 256;
2477 emitByte(b);
2479 } else {
2480 int32_t res = getExprArg(&defined, &fixuptype);
2482 if (pass > 0 && !defined) fatal("undefined operand");
2483 res += defIncr;
2484 if (isWord) {
2485 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
2486 if (res < 0) res += 65536;
2487 if (isWord == 1) {
2488 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 2);
2489 emitWord(res);
2490 } else {
2491 /* reversed word */
2492 switch (fixuptype) {
2493 case UR_FIXUP_WORD: /* swapped bytes */
2494 urasm_fixup_operand(NULL, pc, disp, UR_FIXUP_HIBYTE, 1);
2495 urasm_fixup_operand(NULL, pc, disp+1, UR_FIXUP_LOBYTE, 1);
2496 break;
2497 case UR_FIXUP_LOBYTE:
2498 case UR_FIXUP_HIBYTE:
2499 warningMsg("non-word fixup for reversed word; wtf?!");
2500 break;
2501 default: break;
2503 emitRWord(res);
2505 } else {
2506 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
2507 if (fixuptype != UR_FIXUP_NONE) {
2508 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2509 urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2511 if (res < 0) res += 256;
2512 emitByte(res);
2515 if (!eatComma()) break;
2517 return PI_CONT_LINE;
2520 static int piDEFB (void) { return piDEFBW(0); }
2521 static int piDEFW (void) { return piDEFBW(1); }
2522 static int piDEFR (void) { return piDEFBW(2); }
2525 static int piDEFS (void) {
2526 for (;;) {
2527 int32_t bt, f;
2528 int defined = 0, fixuptype = UR_FIXUP_NONE;
2529 int32_t res = getExprArg(&defined, &fixuptype);
2531 if (pass > 0 && !defined) fatal("undefined operand");
2532 if (res < 1 || res > 65535) fatal("invalid number of repetitions: %d", res);
2533 if (*currLine && currLine[0] == ',') {
2534 (void)eatComma();
2535 bt = getExprArg(&defined, NULL);
2536 if (pass > 0 && !defined) fatal("undefined operand");
2537 bt += defIncr;
2538 if (bt < -128 || bt > 255) fatal("invalid operand value: %d", res);
2539 if (bt < 0) bt += 256;
2540 if (fixuptype != UR_FIXUP_NONE) {
2541 if (fixuptype == UR_FIXUP_WORD) warningMsg("invalid fixup for operand");
2543 for (f = 0; f < res; ++f) {
2544 if (fixuptype != UR_FIXUP_NONE) urasm_fixup_operand(NULL, pc, disp, fixuptype, 1);
2545 emitByte(bt);
2547 } else {
2548 pc += res; disp += res;
2550 if (!eatComma()) break;
2552 return PI_CONT_LINE;
2556 /* bit 0: put '\0' */
2557 /* bit 1: set bit 7 of last byte */
2558 /* bit 2: put length */
2559 static int piDEFSTR (int type) {
2560 for (;;) {
2561 if (isStrArg()) {
2562 int f, len = 0;
2563 char *res = getStrArg(&len);
2565 if (type&0x04) {
2566 if (len > 255) fatal("string too long");
2567 emitByte(len);
2569 for (f = 0; f < len; ++f) {
2570 uint8_t b = (uint8_t)res[f];
2572 if ((type&0x02) && f == len-1) b |= 0x80;
2573 emitByte(b);
2575 if (type&0x01) emitByte(0);
2576 } else {
2577 int defined = 1;
2578 int32_t v = getExprArg(&defined, NULL);
2580 if (pass > 0 && !defined) fatal("undefined expression");
2581 if (!defined) v = 0; else v += defIncr;
2582 if (v < -128 || v > 255) fatal("invalid operand value: %d", v);
2583 if (v < 0) v += 256;
2584 emitByte(v);
2586 if (!eatComma()) break;
2588 return PI_CONT_LINE;
2592 static int piDEFM (void) { return piDEFSTR(0x00); }
2593 static int piDEFZ (void) { return piDEFSTR(0x01); }
2594 static int piDEFX (void) { return piDEFSTR(0x02); }
2595 static int piDEFC (void) { return piDEFSTR(0x04); }
2598 ///////////////////////////////////////////////////////////////////////////////
2599 // INCBIN
2601 /* INCBIN "name"[,maxlen] */
2602 static int piINCBIN (void) {
2603 int system = 0;
2604 char *fn, qCh;
2605 uint8_t bt;
2606 FILE *fl;
2607 int maxlen = 65536;
2608 char *args = currLine;
2610 if (!currLine[0]) fatal("INCBIN without file name");
2611 if (isStrArg()) {
2612 qCh = *args++;
2613 system = 0;
2614 } else if (currLine[0] == '<') {
2615 qCh = '>'; ++args;
2616 system = 1;
2617 } else {
2618 qCh = 0;
2619 system = 0;
2621 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2622 if (!fn[0]) fatal("INCBIN: empty file name");
2623 memmove(currLine, args, strlen(args)+1);
2624 // maxlen
2625 if (currLine[0] == ',') {
2626 int defined = 1;
2627 (void)eatComma();
2628 maxlen = getOneExprArg(&defined, NULL);
2629 if (!defined) fatal("INCBIN: undefined maxlen");
2630 if (maxlen < 1) return 1; // nothing to do
2632 // now fix name
2633 if (system) {
2634 sprintf(currLine, "%s/%s", sysIncludeDir, fn);
2635 } else {
2636 sprintf(currLine, "%s", fn);
2639 fl = fopen(currLine, "rb");
2640 if (!fl) fatal("INCBIN: file not found: %s", currLine);
2641 while (maxlen-- > 0) {
2642 int res = fread(&bt, 1, 1, fl);
2644 if (!res) break;
2645 if (res != 1) { fclose(fl); fatal("INCBIN: error reading file: %s", currLine); }
2646 emitByte(bt);
2648 fclose(fl);
2649 return PI_SKIP_LINE;
2653 ///////////////////////////////////////////////////////////////////////////////
2654 // INCLUDE
2656 /* INCLUDE "name" */
2657 static int piINCLUDE (void) {
2658 int system = 0;
2659 char *fn, qCh;
2660 char *args = currLine;
2662 if (!currLine[0]) fatal("INCLUDE without file name");
2663 if (isStrArg()) {
2664 qCh = *args++;
2665 system = 0;
2666 } else if (currLine[0] == '<') {
2667 qCh = '>'; ++args;
2668 system = 1;
2669 } else {
2670 qCh = 0;
2671 system = 0;
2673 fn = parseStr(&args, qCh, NULL); // i don't care about string length here
2674 if (!fn[0]) fatal("INCLUDE: empty file name");
2676 if (asmTextInclude(fn, system) != 0) fatal("INCLUDE: some shit happens!");
2677 return PI_SKIP_LINE;
2681 ///////////////////////////////////////////////////////////////////////////////
2682 // MODULE, ENDMODULE
2684 static int piENDMODULE (void) {
2685 if (!curModule) fatal("ENDMODULE without MODULE");
2686 if (currLine[0]) {
2687 char *mn = getOneLabelArg();
2688 if (strcmp(mn, curModule->name)) fatal("invalid module name in ENDMODULE: %s", mn);
2690 curModule = NULL;
2691 return PI_SKIP_LINE;
2695 static int piMODULE (void) {
2696 ModuleInfo *mi;
2697 char *mn;
2698 SourceLine *ol = curSrcLine;
2699 int inum;
2701 if (curModule) fatal("no nested modules allowed");
2702 mn = getOneLabelArg();
2703 if (!urasm_is_valid_name(mn)) fatal("invalid module name: %s", mn);
2704 mi = moduleFind(mn);
2705 //printf("[%s] %p\n", mn, mi);
2706 if (mi) {
2707 if (mi->seen) {
2708 if (strcmp(mi->fname, curSrcLine->fname)) fatal("duplicate module definition; previous was in %s", mi->fname);
2709 /* skip module */
2710 nextSrcLine(); /* skip "MODULE" line */
2711 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MODULE", "ENDMODULE", NULL))) {
2712 setCurSrcLine(ol);
2713 fatal("no ENDMODULE");
2715 if (inum == 0) fatal("no nested modules allowed");
2716 curModule = mi;
2717 //skipInstruction(); //k8:wtf?!
2718 return piENDMODULE();
2720 } else {
2721 mi = moduleAdd(mn, curSrcLine->fname);
2723 mi->seen = 1;
2724 curModule = mi;
2725 return PI_SKIP_LINE;
2729 /* Z80, Z80N */
2730 static int piMODEL (void) {
2731 char *mn = getOneIdArgLo();
2732 if (strSkipSpaces(currLine)[0]) fatal("only one model name expected");
2733 if (strcmp(mn, "z80") == 0) urasm_allow_zxnext = 0;
2734 else if (strcmp(mn, "z80a") == 0) urasm_allow_zxnext = 0;
2735 else if (strcmp(mn, "z80n") == 0) urasm_allow_zxnext = 1;
2736 else if (strcmp(mn, "z80next") == 0) urasm_allow_zxnext = 1;
2737 else if (strcmp(mn, "zxnext") == 0) urasm_allow_zxnext = 1;
2738 else fatal("invalid model name: %s", mn);
2739 return PI_SKIP_LINE;
2743 ///////////////////////////////////////////////////////////////////////////////
2744 // DUP, EDUP
2746 static int piEDUP (void) {
2747 fatal("EDUP without DUP");
2748 checkOperatorEnd();
2749 return PI_SKIP_LINE;
2753 static int piDUP (void) {
2754 int defined = 1, dupCnt = 1, inum;
2755 SourceLine *stline, *eline = NULL;
2756 int32_t cnt = getOneExprArg(&defined, NULL);
2758 if (!defined) fatal("DUP: counter must be defined");
2759 if (cnt > 65535) fatal("DUP: counter too big: %d", cnt);
2760 if (cnt < 0) warningMsg("DUP: counter too small: %d", cnt);
2761 // now find corresponding EDUP
2762 // note that we should skip nested DUPs
2763 nextSrcLine(); // skip ourself
2764 stline = curSrcLine;
2765 while (curSrcLine) {
2766 if (!setCurSrcLine(findNextInstructionFromList(&inum, "DUP", "EDUP", NULL))) break;
2767 // ok, we found something; what is it?
2768 if (inum == 0) {
2769 // new DUP, skip it
2770 ++dupCnt;
2771 nextSrcLine(); // skip DUP
2772 } else {
2773 // EDUP
2774 if (--dupCnt == 0) {
2775 // gotcha!
2776 eline = curSrcLine;
2777 break;
2779 nextSrcLine(); // skip EDUP
2782 if (!eline) { setCurSrcLine(stline); fatal("no EDUP"); }
2783 // now repeat that lines
2784 while (cnt-- > 0) {
2785 setCurSrcLine(stline);
2786 while (curSrcLine != eline) processCurrentLine();
2788 return PI_SKIP_LINE;
2792 ///////////////////////////////////////////////////////////////////////////////
2793 // IF, ENDIF
2795 static int ifCount = 0;
2798 // results:
2799 // -1: error (should not happen)
2800 // 0: successfully complete
2801 // 1: stopped *AT* "ELSE"
2802 // 2: stopped *AT* "ELSEIF"
2803 static int ifSkipToEndIfOrElse (int wholeBody) {
2804 int inum, wasElse = 0;
2805 SourceLine *oline;
2807 while (curSrcLine) {
2808 if (!setCurSrcLine(findNextInstructionFromList(&inum, "IF", "ELSE", "ENDIF", "ELSEIF", "MACRO", "ENDM", "IFX", "ELSEIFX", NULL))) break;
2809 switch (inum) {
2810 case 0: /* if */
2811 case 6: /* ifx */
2812 nextSrcLine(); // skip IF
2813 ifSkipToEndIfOrElse(1); // and recurse
2814 nextSrcLine(); // skip ENDIF
2815 break;
2816 case 1: /* else */
2817 if (wasElse) fatal("duplicate ELSE");
2818 if (!wholeBody) return 1;
2819 wasElse = 1;
2820 nextSrcLine(); // skip ELSE
2821 break; // and continue
2822 case 2: /* endif */
2823 return 0; // and exit
2824 case 3: /* elif */
2825 case 7: /* elifx */
2826 if (wasElse) fatal("ELSEIF in ELSE");
2827 if (!wholeBody) return 2;
2828 nextSrcLine(); // skip ELSEIF
2829 break; // and continue
2830 case 4: /* macro */
2831 // skip it as a whole
2832 nextSrcLine(); // skip MACRO
2833 for (;;) {
2834 oline = curSrcLine;
2835 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) { setCurSrcLine(oline); fatal("invalid MACRO"); }
2836 if (inum == 1) break;
2837 fatal("invalid nested MACRO");
2839 nextSrcLine(); // skip ENDM
2840 break;
2841 case 5: /* endm */
2842 fatal("unexpected ENDM");
2845 fatal("IF without ENDIF");
2846 return -1;
2850 static int piENDIF (void) {
2851 /*!fprintf(stderr, "ENDIF(%d): LINE <%s> (%d)\n", ifCount, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2852 if (--ifCount < 0) fatal("ENDIF without IF");
2853 checkOperatorEnd();
2854 return PI_SKIP_LINE;
2858 static int piELSE (void) {
2859 if (--ifCount < 0) fatal("ELSE without IF");
2860 nextSrcLine(); // skip ELSE
2861 ifSkipToEndIfOrElse(1);
2862 return PI_SKIP_LINE;
2865 static int piELSEIF (void) {
2866 if (--ifCount < 0) fatal("ELSEIF without IF");
2867 nextSrcLine(); // skip ELSEIF
2868 ifSkipToEndIfOrElse(1);
2869 return PI_SKIP_LINE;
2873 static int piELSEIFX (void) {
2874 if (--ifCount < 0) fatal("ELSEIFX without IF");
2875 nextSrcLine(); // skip ELSEIFX
2876 ifSkipToEndIfOrElse(1);
2877 return PI_SKIP_LINE;
2881 static int piIFAll (int isIfX) {
2882 int defined = 1;
2883 int ooo = lblOptMakeU2;
2884 lblOptMakeU2 = (isIfX ? 1 : 0);
2885 int32_t cond = getOneExprArg(&defined, NULL);
2886 lblOptMakeU2 = ooo;
2888 if (!defined) {
2889 if (!isIfX) fatal("IF: condition must be defined");
2890 cond = 0; // for IFX: 0 if there is any undefined label
2892 if (cond) {
2893 // ok, do it until ELSE/ELSEIF/ENDIF
2894 ++ifCount;
2895 return PI_SKIP_LINE;
2897 for (;;) {
2898 int r;
2899 char *args;
2901 nextSrcLine(); // skip last instruction
2902 // skip until ELSE/ELSEIF/ENDIF
2903 r = ifSkipToEndIfOrElse(0);
2904 /*!fprintf(stderr, "ELSEIF(%d): 000: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2905 if (r == 0) break; // ENDIF
2906 if (r == 1) { ++ifCount; break; } // ELSE
2907 // ELSEIF, do condition
2908 args = strIsCommand("ELSEIF", currLine);
2909 if (args) {
2910 isIfX = 0;
2911 } else {
2912 if ((args = strIsCommand("ELSEIFX", currLine)) == NULL) fatal("internal error in conditionals"); // the thing that should not be
2913 isIfX = 1;
2915 memmove(currLine, args, strlen(args)+1);
2916 /*!fprintf(stderr, "ELSEIF(%d): 001: LINE <%s> (%d)\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0));*/
2917 cond = getOneExprArg(&defined, NULL);
2918 /*!fprintf(stderr, "ELSEIF(%d): 002: LINE <%s> (%d); cond=%d\n", r, curLine, (curSrcLine ? curSrcLine->lineNo : 0), cond);*/
2919 if (!defined) {
2920 if (!isIfX) fatal("ELSEIF: condition must be defined");
2921 cond = 0; // for IFX: 0 if there is any undefined label
2923 if (cond) { ++ifCount; break; } // condition is true
2925 return PI_SKIP_LINE;
2929 static int piIF (void) { return piIFAll(0); }
2930 static int piIFX (void) { return piIFAll(1); }
2933 ///////////////////////////////////////////////////////////////////////////////
2934 // macro processor
2935 ///////////////////////////////////////////////////////////////////////////////
2937 what i did with MACRO is the brain-damaged cheating all the way.
2939 first, i will collect the MACRO body and remember it (removing it
2940 from the main source code, so second pass will not see it).
2942 second, when the macro is used, i will:
2943 * insert the whole macro body in place (label resolver will
2944 fix "..lbl" labels for us)
2945 * let the asm play with it
2947 this is not the best scheme, but it is fairly simple and it works.
2950 // will be called when parser encounters term starting with '=' or '*'
2951 // first term char will not be skipped
2952 // must return pointer to the first char after expression end
2953 static const char *getValueCB (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
2954 char name[257];
2955 char *p = name;
2957 if (curmacro == NULL) fatal("'=' outside of macro");
2958 if (*expr++ != '=') fatal("'=' expected!");
2960 expr = strSkipSpaces(expr);
2961 while (*expr) {
2962 if (isAlphaDigit(*expr) || *expr == '_') {
2963 if (p-name > 250) fatal("id too long");
2964 *p++ = *expr++;
2965 continue;
2967 break;
2969 *p = 0;
2971 expr = strSkipSpaces(expr);
2972 for (int f = 0; f < curmacro->mac->argc; ++f) {
2973 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
2974 if (*expr == '[') {
2975 urasm_exprval_t v;
2976 int l = (int)strlen(curmacro->argvals[f]);
2977 ++expr; // skip "["
2978 urasm_exprval_init(&v);
2979 expr = urasm_expr_ex(&v, expr, addr, &donteval, defined, error);
2980 if (*error) return expr;
2981 if (expr == NULL || *expr != ']' || v.str != NULL) { *error = UR_EXPRERR_FUNC; return expr; }
2982 ++expr;
2983 if (v.val < 0) v.val += l;
2984 if (v.val < 0 || v.val >= l) {
2985 res->val = '?';
2986 } else {
2987 res->val = (unsigned char)(curmacro->argvals[f][v.val]);
2989 return expr;
2990 } else {
2991 urasm_expr_ex(res, curmacro->argvals[f], addr, &donteval, defined, error);
2992 return expr;
2997 fatal("unknown macro variable: '%s'", name);
3001 //!0: error; oprlen is opr size (w/o ending 0), it's 255 for now
3002 // opr starts with '=' (invariant)
3003 static int expandCB (char *opr, int oprlen) {
3004 char name[257], *p = name, *op = opr;
3006 if (curmacro == NULL) fatal("'=' outside of macro");
3007 if (*op++ != '=') fatal("'=' expected!"); // just in case
3008 //fprintf(stderr, "expand: [%s]\n", opr);
3010 if (!isAlpha(op[0])) return 0; // nothing to do
3012 // copy argument name
3013 while (*op) {
3014 if (isAlphaDigit(*op) || *op == '_') {
3015 if (p-name > 250) fatal("id too long");
3016 *p++ = *op++;
3017 continue;
3019 break;
3021 *p = 0;
3023 // expand argument? we only need to expand `=arg[n]`
3024 op = strSkipSpaces(op);
3025 if (op[0] != '[') return 0;
3027 for (int f = 0; f < curmacro->mac->argc; ++f) {
3028 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3029 if (*op != '[') abort(); // assertion, just in case
3030 // replace argument with character, or with literal value
3031 // used for `=regpair[0]` or `=regpair[]`
3032 if (op[1] == ']') {
3033 op += 2;
3034 const int l = (int)strlen(curmacro->argvals[f]);
3035 const int lleft = (int)strlen(op);
3036 char *tmp = malloc(l+lleft+8);
3037 snprintf(tmp, l+lleft+8, "%s%s", curmacro->argvals[f], op);
3038 if ((int)strlen(tmp) > oprlen) {
3039 free(tmp);
3040 return -1;
3042 strcpy(opr, tmp);
3043 free(tmp);
3044 return 0;
3045 } else {
3046 urasm_exprval_t v;
3047 int error = 0, defined = 1, donteval = 0;
3048 ++op; // skip '['
3049 urasm_exprval_init(&v);
3050 op = (char *)urasm_expr_ex(&v, op, pc, &donteval, &defined, &error);
3051 if (error) return -1;
3052 // result should be a number
3053 if (op == NULL || *op != ']' || v.str != NULL) return -1;
3054 ++op; // skip ']'
3055 // it is guaranteed to have more than one char in opr
3056 // so we can simply put char from argument value, and remove other expression chars
3057 const int l = (int)strlen(curmacro->argvals[f]);
3058 if (v.val < 0) v.val += l; // negative: indexing from the end
3059 if (v.val < 0 || v.val >= l) fatal("index %d is out of bounds for macro argument '%s'", v.val, curmacro->mac->argnames[f]);
3060 // copy char
3061 opr[0] = curmacro->argvals[f][v.val];
3062 // remove other chars
3063 memmove(opr+1, op, strlen(op)+1);
3065 return 0;
3069 fatal("unknown macro variable: '%s'", name);
3073 // main macro expander
3074 static void processMacro (MacroDef *mc) {
3075 SourceLine *oldcurline = curSrcLine;
3076 CurMacroDef cm;
3078 //fprintf(stderr, "processMacro: [%s] (%d)\n", mc->name, curmacronum);
3079 //for (SourceLine *l = mc->lines; l != NULL; l = l->next) fprintf(stderr, "*[%s]\n", l->line);
3081 // parse macro arguments
3082 cm.mac = mc;
3083 for (unsigned f = 0; f < MAX_MACRO_ARGS; ++f) cm.argvals[f] = NULL;
3084 int currArg = 0;
3085 for (;;) {
3086 removeSpaces();
3087 // do we have more arguments?
3088 if (!currLine[0]) break;
3089 // check for argument name (this is purely cosmetic thing)
3090 // use like this: "mcall :argname=[value]" (value may be omited for default)
3091 if (currLine[0] == ':' && isAlpha(currLine[1])) {
3092 int pos = 2;
3093 while (isAlphaDigit(currLine[pos]) || currLine[pos] == '_') ++pos;
3094 //hack!
3095 const char svch = currLine[pos];
3096 currLine[pos] = 0;
3097 if (strcasecmp(currLine+1, mc->argnames[currArg]) != 0) {
3098 // out-of-order, find proper argument and rewind to it
3099 currArg = -1;
3100 for (int c = 0; c < mc->argc; ++c) {
3101 if (strcasecmp(currLine+1, mc->argnames[c]) == 0) {
3102 currArg = c;
3103 break;
3106 if (currArg < 0) fatal("macro '%s' has no argument named '%s'", mc->name, currLine+1);
3108 currLine[pos] = svch;
3109 // remove argument name
3110 memmove(currLine, currLine+pos, strlen(currLine+pos)+1);
3111 removeSpaces();
3112 // check for '='
3113 if (currLine[0] != '=') fatal("expected '=' after argument name");
3114 // remove '='
3115 memmove(currLine, currLine+1, strlen(currLine));
3116 removeSpaces();
3118 // too many args?
3119 if (currArg >= mc->argc) fatal("too many arguments to macro '%s'", mc->name);
3120 // check for default value
3121 if (currLine[0] == ',') {
3122 if (mc->argdefaults[currArg] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[currArg]);
3123 cm.argvals[currArg] = strdup(mc->argdefaults[currArg]);
3124 memmove(currLine, currLine+1, strlen(currLine));
3125 } else {
3126 // skip argument (so we will know its length, and will be able to copy it)
3127 char *e = skipMacroArg(currLine);
3128 //fprintf(stderr, "*[%s]\n*[%s]\n", curLine, e);
3129 const char ech = *e;
3130 if (ech != 0) {
3131 if (ech == ':') fatal("invalid invocation of macro '%s' (only one macro per line allowed)", mc->name);
3132 if (ech != ',') fatal("invalid invocation of macro '%s'", mc->name);
3134 *e = 0;
3135 cm.argvals[currArg] = strdup(currLine);
3136 // strip trailing spaces
3137 strTrimRight(cm.argvals[currArg]);
3138 if (ech) {
3139 *e = ech;
3140 memmove(currLine, e+1, strlen(e));
3141 } else {
3142 currLine[0] = 0;
3145 ++currArg;
3147 // check for line end
3148 removeSpaces();
3149 if (currLine[0]) fatal("invalid macro invocation");
3151 // setup default argument values
3152 for (int f = 0; f < mc->argc; ++f) {
3153 //fprintf(stderr, "MAC: %s; arg #%d (%s): <%s>\n", mc->name, f, mc->argnames[f], cm.argvals[f]);
3154 if (cm.argvals[f]) continue;
3155 if (mc->argdefaults[f] == NULL) fatal("macro '%s' has no default value for argument '%s'", mc->name, mc->argnames[f]);
3156 cm.argvals[f] = strdup(mc->argdefaults[f]);
3157 //fprintf(stderr, " DEF arg #%d (%s): <%s>\n", f, mc->argnames[f], cm.argvals[f]);
3160 //for (int f = 0; f < mc->argc; ++f) fprintf(stderr, "%d: [%s]\n", f, cm.argvals[f]);
3162 // insert macro code into the source
3163 setCurSrcLine(mc->lines);
3164 curmacro = &cm;
3165 ++curmacronum;
3166 while (curSrcLine != NULL) {
3167 //fprintf(stderr, "*[%s] (%d)\n", curSrcLine->line, curSrcLine->lineNo);
3168 processCurrentLine();
3171 setCurSrcLine(oldcurline);
3172 curmacro = NULL;
3173 nextSrcLine();
3177 static int piMACRO (void) {
3178 char *name;
3179 int argc = 0;
3180 char *argdefaults[MAX_MACRO_ARGS];
3181 char *argnames[MAX_MACRO_ARGS];
3182 SourceLine *stline, *eline = NULL;
3183 MacroDef *mc;
3185 name = strdup(getLabelArg(0));
3186 //fprintf(stderr, "[%s]\n", name);
3187 if (findMacro(name) != NULL) fatal("duplicate MACRO definition: '%s'", name);
3188 if (currLine[0] == ',') memmove(currLine, currLine+1, strlen(currLine));
3189 removeSpaces();
3191 while (currLine[0]) {
3192 if (argc >= MAX_MACRO_ARGS) fatal("too many arguments in MACRO");
3193 if (!isAlpha(currLine[0])) fatal("invalid MACRO definition");
3194 argnames[argc] = strdup(getLabelArg(0));
3195 if (currLine[0] == '=') {
3196 // default value
3197 char *e = strchr(currLine, ','), tch;
3199 if (e == NULL) e = currLine+strlen(currLine);
3200 tch = *e;
3201 *e = 0;
3202 argdefaults[argc] = strdup(currLine+1);
3203 *e = tch;
3204 memmove(currLine, e, strlen(e)+1);
3205 } else {
3206 argdefaults[argc] = NULL;
3208 removeSpaces();
3209 if (currLine[0] == ',') {
3210 memmove(currLine, currLine+1, strlen(currLine));
3211 removeSpaces();
3213 //fprintf(stderr, "%d: [%s] [%s]\n", argc, argnames[argc], argdefaults[argc]);
3214 ++argc;
3217 // now find corresponding ENDM
3218 // note that we should skip nested DUPs
3219 stline = curSrcLine;
3220 nextSrcLine(); // skip ourself
3221 while (curSrcLine) {
3222 int inum;
3224 if (!setCurSrcLine(findNextInstructionFromList(&inum, "MACRO", "ENDM", NULL))) break;
3225 // ok, we found something; what is it?
3226 if (inum == 0) {
3227 // new MACRO
3228 fatal("no nested MACROs yet");
3229 } else {
3230 // ENDM, gotcha!
3231 eline = curSrcLine;
3232 // kill ENDM
3233 eline->line[0] = 0;
3234 nextSrcLine(); // skip ENDM
3235 break;
3238 if (!eline) { setCurSrcLine(stline); fatal("no ENDM"); }
3240 if ((mc = calloc(1, sizeof(*mc))) == NULL) fatal("out of memory");
3241 mc->name = name;
3242 mc->argc = argc;
3243 for (int f = 0; f < argc; ++f) { mc->argdefaults[f] = argdefaults[f]; mc->argnames[f] = argnames[f]; }
3245 eline->next = NULL;
3246 mc->lines = stline->next;
3247 stline->next = curSrcLine;
3248 stline->line[0] = 0;
3250 mc->next = maclist;
3251 maclist = mc;
3252 setCurSrcLine(stline);
3253 return PI_SKIP_LINE;
3257 static int piENDM (void) {
3258 fatal("ENDM without MACRO");
3259 return PI_SKIP_LINE;
3263 ///////////////////////////////////////////////////////////////////////////////
3264 // line processor
3265 static int optWriteType = 't';
3266 static int optWTChanged = 0;
3269 static void piTapParseLoaderName (void) {
3270 if (eatComma()) {
3271 int len;
3272 if (!isStrArg()) fatal("loader name expected");
3273 char *fn = getStrArg(&len);
3274 if (len > 10) fatal("loader name too long");
3275 memset(tapeLoaderName, ' ', 10);
3276 for (int f = 0; f < len; ++f) tapeLoaderName[f] = fn[f];
3281 static int piMATHMODE (void) {
3282 char *name = getOneLabelArg();
3283 if (strcasecmp(name, "OLD") == 0) urasm_use_old_priorities = 1;
3284 else if (strcasecmp(name, "NEW") == 0) urasm_use_old_priorities = 0;
3285 else fatal("invalid math mode; NEW or OLD expected");
3286 return PI_SKIP_LINE;
3290 static int piDEFFMT (void) {
3291 char *name;
3293 if (isStrArg()) {
3294 int len = 0;
3295 name = getStrArg(&len);
3296 } else {
3297 name = getLabelArg(1);
3299 if (optWTChanged) return 1;
3301 optRunDMB = optRunTape = optRunSCL = 0;
3302 //optRunSNA = 0;
3304 if (!strcasecmp(name, "SNA") || !strcasecmp(name, "RUNSNA") || !strcasecmp(name, "SNARUN")) {
3305 optWriteType = 's';
3306 if (currLine[0]) fatal("too many expressions");
3307 return PI_SKIP_LINE;
3309 if (!strcasecmp(name, "TAP") || !strcasecmp(name, "TAPE")) {
3310 optWriteType = 't';
3311 piTapParseLoaderName();
3312 return PI_SKIP_LINE;
3314 if (!strcasecmp(name, "RUNTAP") || !strcasecmp(name, "RUNTAPE") || !strcasecmp(name, "TAPERUN")) {
3315 optRunTape = 1;
3316 optWriteType = 't';
3317 piTapParseLoaderName();
3318 return PI_SKIP_LINE;
3320 if (!strcasecmp(name, "BIN") || !strcasecmp(name, "RAW")) {
3321 optWriteType = 'r';
3322 if (currLine[0]) fatal("too many expressions");
3323 return PI_SKIP_LINE;
3325 if (!strcasecmp(name, "DMB") || !strcasecmp(name, "RUNDMB") || !strcasecmp(name, "DMBRUN")) {
3326 optRunDMB = (name[3] != 0);
3327 optWriteType = 'd';
3328 if (currLine[0]) fatal("too many expressions");
3329 return PI_SKIP_LINE;
3331 if (!strcasecmp(name, "NONE") || !strcasecmp(name, "NOTHING")) {
3332 optWriteType = 'n';
3333 if (currLine[0]) fatal("too many expressions");
3334 return PI_SKIP_LINE;
3336 if (!strcasecmp(name, "SCL") || !strcasecmp(name, "RUNSCL") || !strcasecmp(name, "SCLRUN")) {
3337 optWriteType = 'S';
3338 optRunSCL = (name[3] != 0);
3339 piTapParseLoaderName();
3340 return PI_SKIP_LINE;
3342 fatal("invalid default output type: %s", name);
3346 ///////////////////////////////////////////////////////////////////////////////
3347 // line processor
3349 static void processCurrentLine (void) {
3350 if (!curSrcLine) return; // do nothing
3351 loadCurSrcLine();
3352 processLabel();
3353 for (;;) {
3354 char *str, *ee, name[66];
3355 UrAsmOp *op;
3356 int len;
3357 const char *errpos;
3359 removeSpacesAndColons();
3360 // skip spaces and ':'
3361 if (!currLine[0]) { nextSrcLine(); break; }
3362 // try to find and process command
3363 str = currLine; //while (*str && isSpace(*str)) ++str; // skip spaces
3364 // find command end
3365 for (ee = str; *ee && !isSpace(*ee) && *ee != '"' && *ee != '\'' && *ee != ',' && *ee != ':'; ++ee) {}
3366 // get command, if any
3367 if (ee != str && ee-str <= 64) {
3368 MacroDef *mc;
3369 memset(name, 0, sizeof(name));
3370 memmove(name, str, ee-str);
3371 /* known command? */
3372 op = urFindOp(name);
3373 if (op) {
3374 // ok, do it
3375 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3376 memmove(currLine, str, strlen(str)+1);
3377 if (op->fn()) {
3378 nextSrcLine(); // skip it
3379 break;
3381 continue;
3383 /* known macro? */
3384 if ((mc = findMacro(name)) != NULL) {
3385 // ok, do it
3386 str = ee; while (*str && isSpace(*str)) ++str; // skip spaces
3387 memmove(currLine, str, strlen(str)+1);
3388 processMacro(mc);
3389 /* only one macro per line! */
3390 break;
3394 len = urasm_opasm(currLine, pc, disp, &errpos);
3395 if (len < 0) fatalUrLib(len);
3396 pc += len;
3397 disp += len;
3398 if (len >= 0 && errpos) {
3399 memmove(currLine, errpos+1, strlen(errpos));
3400 } else {
3401 nextSrcLine(); // skip it
3402 break;
3408 ///////////////////////////////////////////////////////////////////////////////
3409 // setup instructions
3411 static void registerInstructions (void) {
3412 urAddOp("DISPLAY", piDISPLAY);
3413 urAddOp("DISPLAY0", piDISPLAY0);
3414 urAddOp("DISPLAYA", piDISPLAYA);
3415 urAddOp("DISPHEX", piDISPHEX);
3416 urAddOp("DISPHEX0", piDISPHEX0);
3417 urAddOp("DISPHEXA", piDISPHEXA);
3419 urAddOp("DEFFMT", piDEFFMT);
3420 urAddOp("$MODEL", piMODEL);
3422 urAddOp("MACRO", piMACRO);
3423 urAddOp("ENDM", piENDM);
3425 urAddOp("ORG", piORG);
3426 urAddOp("DISP", piDISP);
3427 urAddOp("ENDDISP", piENDDISP);
3428 urAddOp("PHASE", piDISP);
3429 urAddOp("DEPHASE", piENDDISP);
3430 urAddOp("UNPHASE", piENDDISP);
3431 urAddOp("ALIGN", piALIGN);
3432 urAddOp("DISPALIGN", piDISPALIGN);
3433 urAddOp("PHASEALIGN", piDISPALIGN);
3434 urAddOp("ENT", piENT);
3435 urAddOp("CLR", piCLR);
3436 urAddOp("RESERVE", piRESERVE);
3438 urAddOp("INCLUDE", piINCLUDE);
3439 urAddOp("INCBIN", piINCBIN);
3441 urAddOp("MODULE", piMODULE);
3442 urAddOp("ENDMODULE", piENDMODULE);
3444 urAddOp("DUP", piDUP);
3445 urAddOp("EDUP", piEDUP);
3447 urAddOp("IF", piIF);
3448 urAddOp("IFX", piIFX);
3449 urAddOp("ELSE", piELSE);
3450 urAddOp("ELSEIF", piELSEIF);
3451 urAddOp("ELSEIFX", piELSEIFX);
3452 urAddOp("ENDIF", piENDIF);
3454 urAddOp("DEFINCR", piDEFINCR);
3455 urAddOp("DEFB", piDEFB); urAddOp("DB", piDEFB);
3456 urAddOp("DEFW", piDEFW); urAddOp("DW", piDEFW);
3457 urAddOp("DEFR", piDEFR); urAddOp("DR", piDEFR);
3458 urAddOp("DEFS", piDEFS); urAddOp("DS", piDEFS);
3459 urAddOp("DEFM", piDEFM); urAddOp("DM", piDEFM);
3460 urAddOp("DEFZ", piDEFZ); urAddOp("DZ", piDEFZ);
3461 urAddOp("DEFX", piDEFX); urAddOp("DX", piDEFX);
3462 urAddOp("DEFC", piDEFC); urAddOp("DC", piDEFC);
3464 urAddOp("$ERROR", piERROR);
3465 urAddOp("$WARNING", piWARNING);
3467 urAddOp("$PRINTF", piPRINTF);
3468 urAddOp("$PRINTF0", piPRINTF0);
3469 urAddOp("$PRINTFA", piPRINTFA);
3471 urAddOp("$MATHMODE", piMATHMODE);
3475 ///////////////////////////////////////////////////////////////////////////////
3476 // !0: invalid label
3478 static inline void fnSkipSpaces (const char *expr) {
3479 while (*expr && isSpace(*expr)) ++expr;
3480 return expr;
3485 static inline char fnNextChar (const char *expr) {
3486 while (*expr && isSpace(*expr)) ++expr;
3487 return *expr;
3491 #define FN_SKIP_BLANKS do { \
3492 while (*expr && isSpace(*expr)) ++expr; \
3493 } while (0)
3495 #define FN_CHECK_END do { \
3496 while (*expr && isSpace(*expr)) ++expr; \
3497 if (expr[0] != ')') { *error = UR_EXPRERR_FUNC; return expr; } \
3498 ++expr; \
3499 } while (0)
3502 #define FN_CHECK_COMMA do { \
3503 while (*expr && isSpace(*expr)) ++expr; \
3504 if (expr[0] != ',') { *error = UR_EXPRERR_FUNC; return expr; } \
3505 ++expr; \
3506 } while (0)
3509 static const char *readLabelName (char *buf, const char *expr) {
3510 int pos = 0;
3512 while (*expr && isSpace(*expr)) ++expr;
3513 for (;;) {
3514 char ch = *expr++;
3516 if (pos >= 128) return NULL;
3517 if (ch == ')') { --expr; break; }
3518 if (!ch) break;
3519 if (isAlphaDigit(ch) || ch == '$' || ch == '.' || ch == '_' || ch == '@') {
3520 buf[pos++] = ch;
3521 } else {
3522 break;
3525 if (pos < 1) return NULL;
3526 buf[pos] = '\0';
3527 if (!urasm_is_valid_name(buf)) return NULL;
3528 return expr;
3532 static const char *fnDefKn (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error, int qtype) {
3533 char lbl[130];
3535 if ((expr = readLabelName(lbl, expr)) == NULL) { *error = UR_EXPRERR_LABEL; return NULL; }
3536 FN_CHECK_END;
3537 if (!donteval) res->val = (isLabelDefinedOrKnown(lbl, addr, qtype) != 0);
3538 return expr;
3541 static const char *fnDefined (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) { return fnDefKn(res, expr, addr, donteval, defined, error, UR_QTYPE_DEFINED); }
3542 static const char *fnKnown (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) { return fnDefKn(res, expr, addr, donteval, defined, error, UR_QTYPE_KNOWN); }
3545 static const char *fnAligned256 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3546 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3547 FN_CHECK_END;
3548 if (!donteval) res->val = (res->val%256 ? 0 : 1);
3549 return expr;
3553 static const char *fnSameSeg (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3554 urasm_exprval_t v0, v1;
3556 urasm_exprval_init(&v0);
3557 urasm_exprval_init(&v1);
3558 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
3559 if (*error) return expr;
3560 FN_CHECK_COMMA;
3561 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
3562 if (*error) return expr;
3563 FN_CHECK_END;
3564 if (!donteval) res->val = (v0.val/256 == v1.val/256);
3565 return expr;
3569 static const char *fnAlign (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3570 urasm_exprval_t v0, v1;
3571 urasm_exprval_init(&v0);
3572 urasm_exprval_init(&v1);
3573 expr = urasm_expr_ex(&v0, expr, addr, &donteval, defined, error);
3574 if (*error) return expr;
3575 FN_SKIP_BLANKS;
3576 if (fnNextChar(expr) == ',') {
3577 FN_CHECK_COMMA;
3578 expr = urasm_expr_ex(&v1, expr, addr, &donteval, defined, error);
3579 if (*error) return expr;
3580 } else {
3581 v1.val = 256;
3583 FN_CHECK_END;
3584 if (!donteval) {
3585 if (v1.val < 1) { *error = UR_EXPRERR_FUNC; return NULL; }
3586 res->val = (v0.val+v1.val-1)/v1.val*v1.val;
3588 return expr;
3592 static const char *fnLow (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3593 const char *ee = expr;
3594 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3595 FN_CHECK_END;
3596 if (!donteval) {
3597 if (res->fixuptype == UR_FIXUP_HIBYTE) {
3598 *error = UR_EXPRERR_FUNC; return ee;
3600 res->fixuptype = UR_FIXUP_LOBYTE;
3601 res->val &= 0xff;
3603 return expr;
3607 static const char *fnHigh (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3608 const char *ee = expr;
3609 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3610 FN_CHECK_END;
3611 if (!donteval) {
3612 if (res->fixuptype == UR_FIXUP_LOBYTE) {
3613 *error = UR_EXPRERR_FUNC; return ee;
3615 res->fixuptype = UR_FIXUP_HIBYTE;
3616 res->val = (res->val>>8)&0xff;
3618 return expr;
3622 static const char *fnAbs (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3623 const char *ee = expr;
3624 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3625 FN_CHECK_END;
3626 if (!donteval) {
3627 if (res->fixuptype != UR_FIXUP_NONE) {
3628 *error = UR_EXPRERR_FUNC; return ee;
3630 res->val = abs(res->val);
3632 return expr;
3636 static const char *fnBSwap (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3637 const char *ee = expr;
3638 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3639 FN_CHECK_END;
3640 if (!donteval) {
3641 if (res->fixuptype != UR_FIXUP_NONE) {
3642 *error = UR_EXPRERR_FUNC; return ee;
3644 res->val = ((res->val>>8)&0xff)|((res->val&0xff)<<8);
3646 return expr;
3650 static const char *fnScrAddr8xn (int nmul, urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3651 int32_t scrbase = 0x4000;
3652 int32_t x, y;
3653 const char *ee = expr;
3654 //fprintf(stderr, "fnScrAddr8xn: n=%d; expr=<%s>\n", nmul, expr);
3655 // first argument is `x`
3656 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3657 x = res->val;
3658 // second argument is `y`
3659 FN_CHECK_COMMA;
3660 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3661 y = res->val*nmul;
3662 // optional third arg is screen base
3663 FN_SKIP_BLANKS;
3664 if (expr[0] == ',') {
3665 FN_CHECK_COMMA;
3666 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3667 scrbase = res->val&0xffff;
3669 FN_CHECK_END;
3670 if (!donteval) {
3671 //urasm_exprval_clear(res);
3672 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
3673 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
3674 if (y < 0 || y > 191) fatal("invalid y coordinate: %d", y/nmul);
3675 res->val = scrbase+
3676 (y/64)*2048+
3677 (y%8)*256+
3678 ((y%64)/8)*32+
3680 //fprintf(stderr, "x=%d; y=%d; addr=#%04X\n", x, y, res->val);
3682 return expr;
3686 static const char *fnScrAddr8x1 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3687 return fnScrAddr8xn(1, res, expr, addr, donteval, defined, error);
3690 static const char *fnScrAddr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3691 return fnScrAddr8xn(8, res, expr, addr, donteval, defined, error);
3695 static const char *fnScrAttr8x8 (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3696 int32_t scrbase = 0x4000;
3697 int32_t x, y;
3698 const char *ee = expr;
3699 // first argument is `x`
3700 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3701 x = res->val;
3702 // second argument is `y`
3703 FN_CHECK_COMMA;
3704 urasm_exprval_clear(res);
3705 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3706 y = res->val;
3707 // optional third arg is screen base
3708 FN_SKIP_BLANKS;
3709 if (expr[0] == ',') {
3710 FN_CHECK_COMMA;
3711 urasm_exprval_clear(res);
3712 expr = urasm_expr_ex(res, expr, addr, &donteval, defined, error);
3713 scrbase = res->val&0xffff;
3715 urasm_exprval_clear(res);
3716 FN_CHECK_END;
3717 if (!donteval) {
3718 if (res->fixuptype != UR_FIXUP_NONE) { *error = UR_EXPRERR_FUNC; return ee; }
3719 if (x < 0 || x > 31) fatal("invalid x coordinate: %d", x);
3720 if (y < 0 || y > 23) fatal("invalid y coordinate: %d", y);
3721 res->val = scrbase+6144+y*32+x;
3723 return expr;
3727 static const char *fnMArgToStr (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3728 const char *ee = expr;
3729 // argument is macro argument name
3730 expr = strSkipSpacesConst(expr);
3731 if (expr[0] != '=') { *error = UR_EXPRERR_FUNC; return ee; }
3732 ++expr;
3733 if (!isAlpha(expr[0]) && expr[0] != '_') { *error = UR_EXPRERR_FUNC; return ee; }
3734 const char *nend = expr+1;
3735 while (isAlphaDigit(*nend) || *nend == '_') ++nend;
3736 if (nend-expr > 127) { *error = UR_EXPRERR_FUNC; return ee; }
3737 char name[128];
3738 memset(name, 0, sizeof(name));
3739 memcpy(name, expr, nend-expr);
3740 expr = nend;
3741 // parse index
3742 int index = 0;
3743 int has_index = 0;
3744 expr = strSkipSpacesConst(expr);
3745 if (expr[0] == '[') {
3746 expr = strSkipSpacesConst(expr+1);
3747 if (expr[0] != ']') {
3748 has_index = 1;
3749 int doneg = 0;
3750 if (expr[0] == '+') ++expr;
3751 else if (expr[0] == '-') { doneg = 1; ++expr; }
3752 index = 0;
3753 while (isDigit(expr[0])) {
3754 index = index*10+(expr[0]-'0');
3755 if (index >= 0x1fffffff) fatal("`margtostr` index too high");
3756 ++expr;
3758 expr = strSkipSpacesConst(expr);
3759 if (doneg) index = -index;
3761 if (expr[0] != ']') fatal("`margtostr` invalid index syntax");
3762 ++expr;
3764 // done
3765 FN_CHECK_END;
3766 if (curmacro == NULL) fatal("`margtostr` outside of macro");
3767 if (!donteval) {
3768 for (int f = 0; f < curmacro->mac->argc; ++f) {
3769 if (strcasecmp(name, curmacro->mac->argnames[f]) == 0) {
3770 // found argument, convert it to string
3771 if (!has_index) {
3772 res->str = realloc(res->str, strlen(curmacro->argvals[f])+1);
3773 strcpy(res->str, curmacro->argvals[f]);
3774 } else {
3775 const size_t slen = strlen(curmacro->argvals[f]);
3776 if (index < 0) {
3777 index = -index;
3778 if (index > slen) fatal("index out of string");
3779 index = (int)slen-index;
3781 if (index >= slen) fatal("index out of string");
3782 res->str = realloc(res->str, 2);
3783 res->str[0] = curmacro->argvals[f][index];
3784 res->str[1] = 0;
3786 // lowercase it
3787 for (char *s = res->str; *s; ++s) *s = toLower(*s);
3788 //fprintf(stderr, "<%s>\n", res->str);
3789 if (res->str[0]) {
3790 if (res->str[1]) {
3791 res->val = ((unsigned char)res->str[0]);
3792 res->val |= ((unsigned char)res->str[1])<<8;
3793 } else {
3794 res->val = (unsigned char)res->str[0];
3796 } else {
3797 res->val = 0;
3799 return expr;
3802 fatal("unknown macro argument '%s'", name);
3804 return expr;
3808 static const char *fnStrLen (urasm_exprval_t *res, const char *expr, uint16_t addr, int donteval, int *defined, int *error) {
3809 expr = strSkipSpacesConst(expr);
3810 int stlen = 0;
3811 switch (expr[0]) {
3812 case '"': case '\'': /* string literal? */
3814 char *a = (char *)expr+1;
3815 (void)parseStr(&a, expr[0], &stlen);
3816 expr = (const char *)a;
3818 break;
3819 default: /* expression */
3821 urasm_exprval_t r;
3822 urasm_exprval_init(&r);
3823 expr = urasm_expr_ex(&r, expr, disp, &donteval, defined, error);
3824 if (*error) fatalUrLib(*error);
3825 if (!r.str) fatal("string expected for `strlen()`");
3826 stlen = (int)strlen(r.str);
3827 urasm_exprval_clear(&r);
3829 break;
3831 FN_CHECK_END;
3832 if (!donteval) {
3833 urasm_exprval_setint(res, stlen);
3835 return expr;
3839 static void registerFunctions (void) {
3840 urasm_expr_register_func("defined", fnDefined);
3841 urasm_expr_register_func("known", fnKnown);
3842 urasm_expr_register_func("aligned256", fnAligned256);
3843 urasm_expr_register_func("align", fnAlign);
3844 urasm_expr_register_func("sameseg", fnSameSeg);
3845 urasm_expr_register_func("low", fnLow);
3846 urasm_expr_register_func("high", fnHigh);
3847 urasm_expr_register_func("abs", fnAbs);
3848 urasm_expr_register_func("bswap", fnBSwap);
3850 urasm_expr_register_func("scraddr8x8", fnScrAddr8x8);
3851 urasm_expr_register_func("scraddr8x1", fnScrAddr8x1);
3852 urasm_expr_register_func("scrattr8x8", fnScrAttr8x8);
3854 urasm_expr_register_func("marg2str", fnMArgToStr);
3855 urasm_expr_register_func("strlen", fnStrLen);
3859 ///////////////////////////////////////////////////////////////////////////////
3860 // preparing another pass
3862 static void initPass (void) {
3863 curSrcLine = asmText;
3864 curModule = NULL;
3865 pc = start_pc;
3866 disp = start_disp;
3867 ent = start_ent;
3868 inTapeBlock = 0;
3869 tapeXorB = 0;
3870 wasOrg = 0;
3871 wasClr = 0;
3872 ifCount = 0;
3873 defIncr = 0;
3874 lblOptMakeU2 = 0;
3875 curmacronum = 0;
3876 lastSeenGlobalLabel = strdup(" [MAIN] ");
3877 modulesResetSeen();
3878 prepareMemory();
3879 urasm_use_old_priorities = 0;
3883 static int posstPass (void) {
3884 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel); lastSeenGlobalLabel = NULL;
3885 if (checkLabels()) return -1;
3886 if (ifCount != 0) fatal("unbalanced IFs");
3887 return 0;
3891 ///////////////////////////////////////////////////////////////////////////////
3892 static int labelCmp (const void *aa, const void *bb) {
3893 if (aa == bb) return 0;
3894 const UrLabelInfo *a = *(const UrLabelInfo **)aa;
3895 const UrLabelInfo *b = *(const UrLabelInfo **)bb;
3896 return
3897 a->value < b->value ? -1 :
3898 a->value > b->value ? 1 :
3903 static void writeLabelsFile (const char *fname) {
3904 if (!fname || !fname[0]) return;
3905 // count labels
3906 int lcount = 0;
3907 for (const UrLabelInfo *ll = labels; ll; ll = ll->next) ++lcount;
3908 UrLabelInfo **larr = NULL;
3909 if (lcount > 0) {
3910 // create labels array
3911 larr = (UrLabelInfo **)malloc(sizeof(UrLabelInfo *)*lcount);
3912 lcount = 0;
3913 for (UrLabelInfo *ll = labels; ll; ll = ll->next, ++lcount) larr[lcount] = ll;
3914 // sort labels
3915 qsort(larr, lcount, sizeof(UrLabelInfo *), &labelCmp);
3917 // write labels
3918 FILE *fo = fopen(fname, "w");
3919 if (lcount > 0) {
3920 for (int f = 0; f < lcount; ++f) {
3921 UrLabelInfo *ll = larr[f];
3922 if (!ll->name || (!isAlpha(ll->name[0]) && ll->name[0] != '@')) continue;
3923 if (ll->value < 0) {
3924 fprintf(fo, "%d %s\n", ll->value, ll->name);
3925 } else {
3926 fprintf(fo, "#%04X %s\n", (unsigned)ll->value, ll->name);
3929 free(larr);
3931 fclose(fo);
3935 ///////////////////////////////////////////////////////////////////////////////
3936 // options
3938 static struct option longOpts[] = {
3939 {"org", required_argument, NULL, 600},
3940 {"define", required_argument, NULL, 601},
3941 {"defzero", required_argument, NULL, 602},
3942 {"outdir", required_argument, NULL, 660},
3943 {"reffile", optional_argument, NULL, 669},
3945 {"sna", 0, NULL, 's'},
3946 {"sna128", 0, NULL, 'S'},
3947 {"tap", 0, NULL, 't'},
3948 {"autotap", 0, NULL, 'T'},
3949 {"raw", 0, NULL, 'r'},
3950 {"autodmb", 0, NULL, 'B'},
3951 {"dmb", 0, NULL, 'b'},
3952 {"none", 0, NULL, 'n'},
3953 {"help", 0, NULL, 'h'},
3954 {"hob", 0, NULL, 'H'},
3955 {"scl", 0, NULL, 'l'},
3956 {"autoscl", 0, NULL, 'L'},
3957 {"fixups", optional_argument, NULL, 'F'},
3959 {NULL, 0, NULL, 0}
3963 static const char *defInFiles[] = {"main.zas", "main.a80", "main.asm", NULL};
3966 static void usage (const char *pname) {
3967 printf(
3968 "usage: %s [options] infile\n"
3969 "default infiles:", pname);
3970 for (int f = 0; defInFiles[f]; ++f) printf(" %s", defInFiles[f]);
3971 printf("\n"
3972 "options:\n"
3973 " -s --sna write 48K .SNA file with autostart\n"
3974 " -S --sna128 write 148K .SNA file with autostart\n"
3975 " -t --tap write .tap file\n"
3976 " -T --autotap write .tap file with basic loader (CLR to set CLEAR)\n"
3977 " -r --raw write raw file(s)\n"
3978 " -b --dmb write DMB file\n"
3979 " -B --autodmb write DMB file with autostart\n"
3980 " -H --hob write HoBeta code file(s)\n"
3981 " -l --scl write SCL TR-DOS archive\n"
3982 " -L --autoscl write autostarting SCL TR-DOS archive\n"
3983 " -n --none write nothing\n"
3984 " -F --fixups write fixup file 'zfixuptable.EXT'\n"
3985 " optional arg: text (default), asm/zas, asmdiff/zasdiff, asmz/zasz\n"
3986 " text: .txt file\n"
3987 " asm: address lists with counters\n"
3988 " asmdiff: 2nd and other addresses are relative, with counters\n"
3989 " asmz: address lists, with 0 end markers\n"
3990 " -h --help this help\n"
3991 "specials:\n"
3992 " --reffile[=name] write labels to reffile, formatted as \"#nnnn NAME\"\n"
3993 " --org xxx set ORG\n"
3994 " --define val perform 'val EQU 1'\n"
3995 " --defzero val perform 'val EQU 0'\n"
3996 " --outdir dir output dir for resulting files (default: current)\n"
4001 ///////////////////////////////////////////////////////////////////////////////
4002 // main
4004 int main (int argc, char *argv[]) {
4005 int res = 0, c;
4006 const char *pname = argv[0];
4007 char *inFile = NULL;
4008 char **defines = NULL, **values = NULL;
4009 int defcount = 0;
4010 int wantref = 0;
4012 initInclideDir();
4014 urasm_getbyte = getByte;
4015 urasm_putbyte = putByte;
4016 urasm_label_by_name = findLabelCB;
4017 urasm_getval = getValueCB;
4018 urasm_expand = expandCB;
4019 urasm_fixup_operand = fixupOperandCB;
4021 //strcpy(tapeLoaderName, "cargador ");
4022 tapeLoaderName[0] = 0;
4024 printf("urasm v%d.%d.%d, compile date: %s %s\n", VERSION_HI, VERSION_MID, VERSION_LO, __DATE__, __TIME__);
4025 while ((c = getopt_long(argc, argv, "sStTrBbnhHFLl", longOpts, NULL)) >= 0) {
4026 switch (c) {
4027 case '?': return 1;
4028 case 'S': /*optRunSNA = 1;*/ optSNA48 = 0; optWriteType = 's'; optWTChanged = 1; break;
4029 case 's': /*optRunSNA = 0;*/ optSNA48 = 1; optWriteType = 's'; optWTChanged = 1; break;
4030 case 'T': optRunTape = 1; optWriteType = 't'; optWTChanged = 1; break;
4031 case 't': optRunTape = 0; optWriteType = 't'; optWTChanged = 1; break;
4032 case 'L': optRunSCL = 1; optWriteType = 'S'; optWTChanged = 1; break;
4033 case 'l': optRunSCL = 0; optWriteType = 'S'; optWTChanged = 1; break;
4034 case 'r': case 'n': optWriteType = c; optWTChanged = 1; break;
4035 case 'b': optRunTape = 0; optRunDMB = 0; optWriteType = 'd'; optWTChanged = 1; break;
4036 case 'B': optRunTape = 0; optRunDMB = 1; optWriteType = 'd'; optWTChanged = 1; break;
4037 case 'h': usage(pname); res = 0; goto earlyerrquit;
4038 case 'H': optWriteType = 'H'; optWTChanged = 1; break;
4039 case 'F':
4040 optWriteFixups = 1;
4041 if (optarg != NULL) {
4042 if (strcmp(optarg, "asm") == 0 || strcmp(optarg, "zas") == 0) {
4043 optFixupType = 1;
4044 } else if (strcmp(optarg, "asmdiff") == 0 || strcmp(optarg, "zasdiff") == 0) {
4045 optFixupType = 2;
4046 } else if (strcmp(optarg, "asmz") == 0 || strcmp(optarg, "zasz") == 0) {
4047 optFixupType = 3;
4048 } else if (strcmp(optarg, "text") == 0) {
4049 optFixupType = 0;
4050 } else {
4051 fprintf(stderr, "FATAL: invalid fixup type: '%s'\n", optarg);
4052 return 1;
4055 break;
4056 case 600: // org
4057 c = atoi(optarg);
4058 //fprintf(stderr, "ORG: %d\n", c);
4059 if (c < 0 || c > 65535) {
4060 fprintf(stderr, "FATAL: invalid ORG: %d\n", c);
4061 return 1;
4063 start_pc = start_disp = start_ent = c;
4064 break;
4065 case 601: // define
4066 //fprintf(stderr, "define: [%s]\n", optarg);
4067 defines = realloc(defines, sizeof(char *)*(defcount+1));
4068 values = realloc(values, sizeof(char *)*(defcount+1));
4069 defines[defcount] = strdup(optarg);
4070 values[defcount] = strdup("1");
4071 ++defcount;
4072 break;
4073 case 602: // defzero
4074 //fprintf(stderr, "defzero: [%s]\n", optarg);
4075 defines = realloc(defines, sizeof(char *)*(defcount+1));
4076 values = realloc(values, sizeof(char *)*(defcount+1));
4077 defines[defcount] = strdup(optarg);
4078 values[defcount] = strdup("0");
4079 ++defcount;
4080 break;
4081 case 660: // outdir
4082 if (optOutputDir != NULL) free(optOutputDir);
4083 optOutputDir = strdup(optarg);
4084 break;
4085 case 669: // reffile
4086 if (refFileName) free(refFileName);
4087 refFileName = (optarg ? strdup(optarg) : NULL);
4088 wantref = 1;
4089 break;
4093 if (optind >= argc) {
4094 // try to find default input file
4095 for (int f = 0; defInFiles[f]; ++f) {
4096 if (!access(defInFiles[f], R_OK)) {
4097 inFile = strdup(defInFiles[f]);
4098 break;
4101 } else {
4102 inFile = strdup(argv[optind]);
4105 if (!inFile || !inFile[0]) {
4106 res = 1;
4107 fprintf(stderr, "ERROR: no input file!\n");
4108 goto earlyerrquit;
4111 if (optOutputDir == NULL) optOutputDir = strdup(".");
4113 registerInstructions();
4114 registerFunctions();
4116 res = asmTextLoad(inFile);
4117 if (!res) {
4118 for (int f = 0; f < defcount; ++f) {
4119 if (labelDoEQU(defines[f], values[f]) != 0) {
4120 fprintf(stderr, "FATAL: can't define label: '%s'\n", defines[f]);
4121 goto errquit;
4125 for (pass = 0; pass <= 1; ++pass) {
4126 initPass();
4127 printf("pass %d\n", pass);
4128 setCurSrcLine(asmText);
4129 if (setjmp(errJP)) { res = 1; break; }
4130 while (curSrcLine) processCurrentLine();
4131 if (posstPass()) { res = 1; break; }
4134 // write result
4135 if (res == 0) {
4136 char *oc = strdup(inFile);
4137 char *pd = strrchr(oc, '.');
4138 if (pd && !strchr(oc, '/')) *pd = '\0';
4139 switch (optWriteType) {
4140 case 's': saveSna(oc, optSNA48); break;
4141 case 't': saveTap(oc); break;
4142 case 'r': saveRaw(oc); break;
4143 case 'd': saveDMB(oc); break;
4144 case 'H': saveHob(oc); break;
4145 case 'S': saveSCL(oc); break;
4147 free(oc);
4148 if (optWriteFixups) writeFixups();
4149 if (wantref) {
4150 /* build ref file name */
4151 if (!refFileName || !refFileName[0]) {
4152 if (refFileName) free(refFileName);
4153 refFileName = malloc(strlen(inFile)+128);
4154 strcpy(refFileName, inFile);
4155 char *ext = strrchr(refFileName, '.');
4156 if (!ext) {
4157 strcat(refFileName, ".ref");
4158 } else {
4159 #ifdef WIN32
4160 char *slash = NULL;
4161 for (char *ts = refFileName; *ts; ++ts) {
4162 if (*ts == '/' || *ts == '\\') slash = ts;
4164 #else
4165 char *slash = strrchr(refFileName, '/');
4166 #endif
4167 if (!slash || slash < ext) {
4168 strcpy(ext, ".ref");
4169 } else {
4170 strcat(refFileName, ".ref");
4174 writeLabelsFile(refFileName);
4175 printf("refs written to '%s'\n", refFileName);
4176 free(refFileName);
4179 } else {
4180 fprintf(stderr, "ERROR: loading error!\n");
4183 errquit:
4184 if (lastSeenGlobalLabel) free(lastSeenGlobalLabel);
4185 clearFixups();
4186 urClearLabels();
4187 modulesClear();
4188 asmTextClear();
4189 urClearOps();
4191 earlyerrquit:
4192 if (inFile) free(inFile);
4193 if (sysIncludeDir) free(sysIncludeDir);
4194 for (int f = defcount-1; f >= 0; --f) {
4195 free(values[f]);
4196 free(defines[f]);
4198 if (defines != NULL) { free(values); free(defines); }
4199 if (optOutputDir != NULL) free(optOutputDir);
4200 return (res ? 1 : 0);