fuck! yet another Iptscrae fix
[urasm.git] / src / urasm.c
blobe833512e4dd85a9c2fc7190ecbbd5d30126b577c
1 // URASM Z80 assembler
2 // coded by Ketmar // Vamprile Avalon
3 // GPLv3 or later
4 //
5 #include <ctype.h>
6 #include <setjmp.h>
7 #include <stdarg.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <unistd.h>
14 #include "iptclib/iptc.h"
16 #include "urasmlib/urasmlib.h"
17 #include "urasmops.h"
18 #include "ursna48.h"
21 ///////////////////////////////////////////////////////////////////////////////
22 // prototypes
24 static int processFile (const char *fname);
27 ///////////////////////////////////////////////////////////////////////////////
28 // compiler variables
30 static char *sysIncludeDir = NULL;
31 static char **knownModules = NULL;
34 static uint8_t memory[65536];
35 static char memused[65536];
36 static uint16_t pc = 0; /* current position to write */
37 static uint16_t disp = 0; /* current 'virtual PC' */
38 static uint16_t ent = 0; /* eng */
39 static int wasOrg = 0;
40 static int pass = 0; /* 0: collect labels; 1: compiling; 2: compiling and fixing labels */
41 static int inTapeBlock = 0;
42 static uint8_t tapeXorB = 0;
43 static uint16_t tapeBlockSizeAddr = 0;
44 static uint16_t tapeBlockHdrAddr = 0;
45 static char *tapeBlockName = NULL;
47 static int optShowOut = 0;
48 static int optRunSNA = 0;
49 static int optTapExt = 0; /* set '.tap' extension (but don't write tape; this for inline tape blocks) */
51 static char *curFileName = NULL;
52 static int curLineNo = 0;
53 #define MAX_LINE_SIZE 16384
54 static char curLine[MAX_LINE_SIZE];
55 static FILE *curFile = NULL;
57 static char *lastSeenLabel = NULL; /* global */
58 static char *curModuleName = NULL;
59 static int modInCurFile = 0; /* module started in current file */
60 static int moduleSkipFlag = 0;
63 ///////////////////////////////////////////////////////////////////////////////
64 // module utils
66 static void clearKnownModules (void) {
67 int f;
68 if (!knownModules) return;
69 for (f = 0; knownModules[f]; ++f) free(knownModules[f]);
70 free(knownModules);
71 knownModules = NULL;
75 static int isKnownModule (const char *str) {
76 int f;
77 if (!knownModules) return 0;
78 for (f = 0; knownModules[f]; ++f) if (!strcmp(knownModules[f], str)) return 1;
79 return 0;
83 static void addKnownModule (const char *str) {
84 int f = 0;
85 char **nn;
86 if (knownModules) {
87 for (f = 0; knownModules[f]; ++f) if (!strcmp(knownModules[f], str)) return;
89 nn = realloc(knownModules, (f+2)*sizeof(char *));
90 if (!nn) abort();
91 knownModules = nn;
92 knownModules[f] = strdup(str);
93 knownModules[f+1] = NULL;
97 ///////////////////////////////////////////////////////////////////////////////
98 // file stack
100 typedef struct FileStack FileStack;
101 struct FileStack {
102 FileStack *prev;
103 FILE *fl;
104 char *name;
105 char lineNo;
107 static FileStack *fstack = NULL;
110 static void clearFileStack (void) {
111 FileStack *c;
112 while (fstack) {
113 c = fstack; fstack = fstack->prev;
114 if (c->fl) fclose(c->fl);
115 if (c->name) free(c->name);
116 free(c);
121 static void popFile (void) {
122 FileStack *c;
124 if (!fstack) abort();
125 curFile = fstack->fl;
126 curFileName = fstack->name;
127 curLineNo = fstack->lineNo;
128 c = fstack; fstack = fstack->prev;
129 free(c);
133 ///////////////////////////////////////////////////////////////////////////////
134 // dup lists
136 typedef struct {
137 int count; // dups left; <1: don't generate code
138 long fpos;
139 int lineNo;
140 } DupChunk;
141 static DupChunk *dupStack = NULL;
142 static int dupStackCnt = 0;
145 static void newDupChunk (int dupcnt) {
146 DupChunk *c, *nn = realloc(dupStack, (dupStackCnt+1)*sizeof(DupChunk));
147 if (!nn) abort();
148 dupStack = nn;
149 c = dupStack+(dupStackCnt++);
150 memset(c, 0, sizeof(DupChunk));
151 c->count = dupcnt;
152 c->lineNo = curLineNo;
153 c->fpos = ftell(curFile);
157 static DupChunk *curDupChunk (void) {
158 if (dupStackCnt < 1) return NULL;
159 return dupStack+(dupStackCnt-1);
163 static void dropDupChunk (void) {
164 if (dupStackCnt > 0) {
165 --dupStackCnt;
170 ///////////////////////////////////////////////////////////////////////////////
171 // string utilities
174 static void trimleft (char *s) {
175 char *p;
176 for (p = s; *p && isspace(*p); ++p) ;
177 memmove(s, p, strlen(p)+1);
182 static void trimright (char *s) {
183 char *p;
184 for (p = s+strlen(s)-1; p >= s && isspace(*p); --p) ;
185 p[1] = '\0';
190 * get operator name
191 * return NULL if there can't be any operator, or
192 * pointer to operator arguments
194 static char *getOpName (char *dest, char *str) {
195 char *e;
197 dest[0] = '\0';
198 while (*str && isspace(*str)) ++str;
199 for (e = str; *e && !isspace(*e); ++e) ;
200 if (e-str > 32 || e == str) return NULL; // too big
201 memmove(dest, str, e-str);
202 dest[e-str] = '\0';
203 for (; *e && isspace(*e); ++e) ;
204 return e;
208 ///////////////////////////////////////////////////////////////////////////////
209 // memory getters and setters
211 static uint8_t getByte (uint16_t addr) {
212 return memory[addr];
216 static void putByte (uint16_t addr, uint8_t b) {
217 if (inTapeBlock) tapeXorB ^= b;
218 //if (pass > 0) printf("%04X: %02X\n", addr, b);
219 memory[addr] = b;
220 memused[addr] = 1;
224 static void putWord (uint16_t addr, uint16_t w) {
225 putByte(addr, w&0xFFU);
226 putByte(addr+1, (w>>8)&0xFFU);
230 ///////////////////////////////////////////////////////////////////////////////
231 // unpackers
233 static void rleUnpack (uint16_t addr, const uint8_t *buf, int buflen) {
234 for (; buflen >= 2; buflen -= 2) {
235 int cnt = *buf++;
236 uint8_t byte = *buf++;
237 //printf("%d %u %u\n", cnt+1, byte, addr);
238 for (; cnt >= 0; --cnt, ++addr) if (!memused[addr]) putByte(addr, byte);
243 static void prepareMemory (int putSNA) {
244 memset(memory, 0, sizeof(memory));
245 memset(memused, 0, sizeof(memused));
246 //if (putSNA) rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
250 static int writeSNA (const char *fname) {
251 char *fn = malloc(strlen(fname)+16);
252 uint8_t regs[27];
253 FILE *fo;
255 sprintf(fn, "%s.sna", fname);
256 fo = fopen(fn, "wb");
257 free(fn);
258 if (!fo) { fprintf(stderr, "ERROR: can't write SNA file: %s.sna\n", fname); return -1; }
259 memmove(regs, ursna48, 27);
260 if (optRunSNA) {
261 /* push new address */
262 uint16_t sp = (uint16_t)regs[23]+(((uint16_t)regs[24])<<8);
263 sp -= 2;
264 putByte(sp, ent&0xFFU);
265 putByte(sp+1, (ent>>8)&0xFFU);
266 regs[23] = sp&0xFFU;
267 regs[24] = (sp>>8)&0xFFU;
269 fwrite(regs, 27, 1, fo);
270 rleUnpack(16384, ursna48+27, sizeof(ursna48)-27);
271 fwrite(memory+16384, 49152, 1, fo);
272 fclose(fo);
273 return 0;
277 ///////////////////////////////////////////////////////////////////////////////
278 // error raisers, etc
280 static jmp_buf errJP;
282 static void __attribute__((format(printf, 1, 2))) errorMsg (const char *fmt, ...) {
283 va_list ap;
285 va_start(ap, fmt);
286 fprintf(stderr, "FATAL at file %s, line %d: ", curFileName, curLineNo);
287 vfprintf(stderr, fmt, ap);
288 va_end(ap);
289 fputc('\n', stderr);
290 fflush(stderr);
294 static void __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) fatal (const char *fmt, ...) {
295 va_list ap;
297 va_start(ap, fmt);
298 fprintf(stderr, "FATAL at file %s, line %d: ", curFileName, curLineNo);
299 vfprintf(stderr, fmt, ap);
300 va_end(ap);
301 fputc('\n', stderr);
302 fflush(stderr);
303 longjmp(errJP, 666);
307 static void __attribute__((noreturn)) fatalPos (int errcode, const char *errpos) {
308 fprintf(stderr, "ERROR at file %s, line %d, position %d: %s\n%s\n", curFileName, curLineNo, errpos-curLine+1, urErrorMessage(errcode), curLine);
309 for (; errpos >= curLine; --errpos) fputc('^', stderr);
310 fputc('\n', stderr);
311 fflush(stderr);
312 longjmp(errJP, 666);
316 ///////////////////////////////////////////////////////////////////////////////
317 // expression utils
319 static int32_t getArg (const char **args, int *defined) {
320 int error = 0;
321 const char *a = *args;
322 int32_t res;
324 while (*a && isspace(*a)) ++a;
325 if (!*a) fatalPos(URA_GENERIC, a);
326 const char *ee = urExpression(&res, a, disp, defined, &error);
327 if (error || (*ee && ee[0] != ';' && ee[0] != ',')) fatalPos(URA_GENERIC, ee);
328 *args = ee;
329 return res;
333 static int32_t getOneArg (const char *args, int *defined) {
334 const char *aa = args;
335 int32_t res = getArg(&aa, defined);
336 if (aa[0] && aa[0] != ';') fatalPos(URA_BAD_OPERAND, aa);
337 return res;
341 /* free() result */
342 static char *getStrArg (const char **args, int *lenp) {
343 int len = 0;
344 char *res = malloc(MAX_LINE_SIZE+128), *p = res, qch;
345 const char *a = *args;
347 while (*a && isspace(*a)) ++a;
348 if (a[0] && (a[0] != '"' && a[0] != '\'')) { free(res); fatalPos(URA_GENERIC, a); }
349 qch = *a++;
350 for (; *a && *a != qch; ++a) {
351 if (*a == '\\') {
352 if (!a[1]) break;
353 switch (a[1]) {
354 case 't': *p++ = '\t'; break;
355 case 'r': *p++ = '\r'; break;
356 case 'n': *p++ = '\n'; break;
357 case 'a': *p++ = '\a'; break;
358 case 'f': *p++ = '\f'; break;
359 case 'v': *p++ = '\v'; break;
360 default: *p++ = a[1]; break;
362 ++a;
363 } else {
364 *p++ = *a;
366 ++len;
368 if (a[0] != qch) { free(res); fatalPos(URA_GENERIC, a); }
369 ++a;
370 while (*a && isspace(*a)) ++a;
371 *p = '\0';
372 if (*a && a[0] != ';' && a[0] != ',') { free(res); fatalPos(URA_GENERIC, a); }
373 *args = a;
374 *lenp = len;
375 return res;
379 static __attribute__((unused)) char *getOneStrArg (const char *args, int *len) {
380 const char *aa = args;
381 char *res = getStrArg(&aa, len);
382 if (aa[0] && aa[0] != ';') { free(res); fatalPos(URA_BAD_OPERAND, aa); }
383 return res;
387 /* free() result */
388 static char *getLabelArg (const char **args) {
389 char *res = malloc(MAX_LINE_SIZE+128), *p = res;
390 const char *a = *args;
392 while (*a && isspace(*a)) ++a;
393 if (!a[0]) { free(res); fatalPos(URA_GENERIC, a); }
394 for (; *a && a[0] != ';' && a[0] != ','; ++a) *p++ = *a;
395 while (*a && isspace(*a)) ++a;
396 for (; p > res && isspace(p[-1]); --p) ;
397 *p = '\0';
398 if (*a && a[0] != ';' && a[0] != ',') { free(res); fatalPos(URA_GENERIC, a); }
399 if (!urIsValidLabelName(res)) { free(res); fatalPos(URA_GENERIC, *args); }
400 *args = a;
401 return res;
405 static char *getOneLabelArg (const char *args) {
406 const char *aa = args;
407 char *res = getLabelArg(&aa);
408 if (aa[0] && aa[0] != ';') { free(res); fatalPos(URA_BAD_OPERAND, aa); }
409 return res;
413 /* free() result */
414 static char *getIncludeArg (const char **args, int *system) {
415 char *res = malloc(MAX_LINE_SIZE+128), *p = res, qch;
416 const char *a = *args;
418 if (system) *system = 0;
419 while (*a && isspace(*a)) ++a;
420 if (a[0] && (a[0] != '"' && a[0] != '\'' && a[0] != '<')) { free(res); fatalPos(URA_GENERIC, a); }
421 qch = *a++;
422 if (qch == '<') {
423 qch = '>';
424 if (system) *system = 1;
426 for (; *a && *a != qch; ++a) {
427 if (*a == '\\') {
428 if (!a[1]) break;
429 switch (a[1]) {
430 case 't': *p++ = '\t'; break;
431 case 'r': *p++ = '\r'; break;
432 case 'n': *p++ = '\n'; break;
433 case 'a': *p++ = '\a'; break;
434 case 'f': *p++ = '\f'; break;
435 case 'v': *p++ = '\v'; break;
436 default: *p++ = a[1]; break;
438 ++a;
439 } else {
440 *p++ = *a;
443 if (a[0] != qch) { free(res); fatalPos(URA_GENERIC, a); }
444 ++a;
445 while (*a && isspace(*a)) ++a;
446 *p = '\0';
447 if (*a && a[0] != ';' && a[0] != ',') { free(res); fatalPos(URA_GENERIC, a); }
448 *args = a;
449 return res;
453 static char *getOneIncludeArg (const char *args, int *system) {
454 const char *aa = args;
455 char *res = getIncludeArg(&aa, system);
456 if (aa[0] && aa[0] != ';') { free(res); fatalPos(URA_BAD_OPERAND, aa); }
457 return res;
461 ///////////////////////////////////////////////////////////////////////////////
462 // label manager
464 static char *fixLocalLabel (const char *name) {
465 // this is local label, let's rename it
466 char *newname = malloc(MAX_LINE_SIZE+1024);
467 if (!newname) fatal("out of memory!");
468 sprintf(newname, "{%s%s}", lastSeenLabel, name);
469 return newname;
473 static int32_t findLabelCB (const char *name, uint16_t addr, int *defined, int *found) {
474 UrLabelInfo *lbl;
475 char *nn;
476 static char n2[(MAX_LINE_SIZE+1024)*2];
478 nn = name[0]=='.'&&lastSeenLabel ? fixLocalLabel(name) : (char *)name;
479 lbl = urFindLabel(nn);
480 if (!lbl && curModuleName && (nn != name || !strchr(name, '.'))) {
481 sprintf(n2, "%s.%s", curModuleName, nn);
482 lbl = urFindLabel(n2);
484 if (!lbl) {
485 if (pass != 0) {
486 errorMsg("using undefined label %s", name);
487 *found = 0;
488 *defined = 0;
489 return 0;
491 if (curModuleName && (nn != name || !strchr(name, '.'))) {
492 sprintf(n2, "%s.%s", curModuleName, nn);
493 lbl = urAddLabel(n2);
494 } else {
495 lbl = urAddLabel(nn);
497 lbl->type = -1;
498 lbl->known = 0;
499 lbl->refLine = curLineNo;
500 lbl->refFile = strdup(curFileName);
501 //printf("new label: [%s]\n", nn);
502 } else {
503 //printf("label reference: [%s]\n", nn);
505 if (nn != name) free(nn);
506 if (lbl) {
507 *found = 1;
508 *defined = lbl->known!=0;
509 return lbl->value;
511 *found = 0;
512 *defined = 0;
513 return 0;
518 * return:
519 * 0: go on
520 * 1: go to the next line
522 static int processLabel (void) {
523 char opname[64], *argstart;
524 int localLbl;
525 /* new label */
526 char *ep, *nn, ch;
527 static char n2[(MAX_LINE_SIZE+1024)*2];
528 UrLabelInfo *lbl;
529 int noLocAff = 0;
530 // collect label
531 for (ep = curLine; *ep && !isspace(*ep) && *ep != ':' && *ep != ';'; ++ep) ;
532 DupChunk *cdup = curDupChunk();
533 if (cdup && cdup->count < 1) {
534 // skip label and following spaces
535 if (*ep == ':') ++ep;
536 for (; *ep && isspace(*ep); ++ep) ;
537 // get command name
538 if (*ep == '=') return 1; // next line
539 if (!(argstart = getOpName(opname, ep))) return 1; // next line
540 if (!strcasecmp(opname, "equ")) return 1; // next line
541 return 0; // go on, do nothing with label
543 if (*ep == ';') *ep = '\0';
544 ch = *ep; *ep = '\0';
545 if (!urIsValidLabelName(curLine)) fatal("invalid label: %s", curLine);
546 if (curLine[0] == '@' && curLine[1]) {
547 /* @... doesn't affect locals */
548 memmove(curLine, curLine+1, strlen(curLine));
549 noLocAff = 1;
550 nn = curLine;
551 } else {
552 nn = curLine[0]=='.'&&lastSeenLabel ? fixLocalLabel(curLine) : curLine;
555 if (curModuleName && (nn != curLine || !strchr(curLine, '.'))) {
556 sprintf(n2, "%s.%s", curModuleName, nn);
557 lbl = urAddLabel(n2);
558 } else {
559 lbl = urAddLabel(nn);
561 if (nn != curLine) {
562 free(nn);
563 localLbl = 1;
564 } else {
565 localLbl = 0;
567 // skip label and following spaces
568 *ep = ch;
569 if (ch == ':') ++ep;
570 for (; *ep && isspace(*ep); ++ep) ;
572 //lbl->known = 1;
573 if (!lbl->refFile) {
574 lbl->refLine = curLineNo;
575 lbl->refFile = strdup(curFileName);
577 //printf("%d: new: [%s]\n", localLbl, lbl->name);
578 // get command name
579 if (*ep == '=') {
580 strcpy(opname, "=");
581 argstart = ++ep;
582 } else {
583 argstart = getOpName(opname, ep);
586 if (!strcmp(opname, "=")) {
587 /* lbl = expr */
588 if (lbl->type != -1 && lbl->type != 0) fatal("duplicate label %s", lbl->name);
589 int defined = 1;
590 int32_t res = getOneArg(argstart, &defined);
591 lbl->type = 0;
592 if (defined) {
593 lbl->value = res;
594 lbl->known = 1;
595 } else {
596 if (pass != 0) fatal("can't calculate label %s", lbl->name);
598 //printf("%s = %d\n", lbl->name, res);
599 return 1;
602 if (pass == 0 && lbl->type != -1) fatal("duplicate label %s", lbl->name);
603 if (!strcasecmp(opname, "equ")) {
604 /* lbl equ expr */
605 int defined = 1;
606 int32_t res = getOneArg(argstart, &defined);
607 lbl->type = 1;
608 if (defined) {
609 lbl->value = res;
610 lbl->known = 1;
611 } else {
612 if (pass != 0) fatal("can't calculate label %s", lbl->name);
614 //printf("%s EQU %d\n", lbl->name, res);
615 return 1;
617 /* code label */
618 if (!localLbl && !noLocAff) {
619 if (lastSeenLabel) free(lastSeenLabel);
620 lastSeenLabel = strdup(lbl->name);
622 lbl->type = 2;
623 lbl->value = disp;
624 lbl->known = 1;
625 memmove(curLine, ep, strlen(ep)+1);
626 return 0;
630 static int checkLabels (void) {
631 UrLabelInfo *c;
632 int wasError = 0;
634 for (c = labels; c; c = c->next) {
635 if (c->type == -1) {
636 fprintf(stderr, "ERROR at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
637 wasError = 1;
640 //if (wasError) longjmp(errJP, 667);
641 return wasError;
645 static void checkUndefineds (void) {
646 UrLabelInfo *c;
648 for (c = labels; c; c = c->next) {
649 if (c->type == -1) {
650 fprintf(stderr, "WARNING at file %s, line %d: referencing undefined label: %s\n", c->refFile, c->refLine, c->name);
652 if (c->type == 0) c->known = -1;
657 ///////////////////////////////////////////////////////////////////////////////
658 // collectors/feeders
660 typedef void (*CollectorFn) (void);
661 typedef int (*FeederFn) (void);
664 static CollectorFn collectorCB = NULL;
665 static FeederFn feederCB = NULL;
668 static CollectorFn *colStack = NULL;
669 static int colStackCnt = 0;
670 static FeederFn *feedStack = NULL;
671 static int feedStackCnt = 0;
674 static __attribute__((unused)) void newCollector (CollectorFn fn) {
675 CollectorFn *nn = realloc(colStack, (colStackCnt+1)*sizeof(CollectorFn));
676 if (!nn) abort();
677 colStack = nn;
678 colStack[colStackCnt++] = collectorCB;
679 collectorCB = fn;
683 static __attribute__((unused)) void newFeeder (FeederFn fn) {
684 FeederFn *nn = realloc(feedStack, (feedStackCnt+1)*sizeof(FeederFn));
685 if (!nn) abort();
686 feedStack = nn;
687 feedStack[feedStackCnt++] = feederCB;
688 feederCB = fn;
692 static __attribute__((unused)) void popCollector (void) {
693 if (colStackCnt-- < 1) abort();
694 collectorCB = colStack[colStackCnt];
698 static __attribute__((unused)) void popFeeder (void) {
699 if (feedStackCnt-- < 1) abort();
700 feederCB = feedStack[feedStackCnt];
704 ///////////////////////////////////////////////////////////////////////////////
705 // pseudoinstructions
707 static int piORG (const char *args) {
708 int defined = 1;
709 int32_t res = getOneArg(args, &defined);
710 if (!defined) fatal("sorry, ORG operand value must be known here");
711 if (res < 0 || res > 65535) fatal("invalid ORG operand value: %d", res);
712 if (inTapeBlock) fatal("sorry, no ORGs in TAPEDATA allowed");
713 pc = disp = res;
714 if (!wasOrg) {
715 wasOrg = 1;
716 ent = res;
718 return 0;
722 static int piDISP (const char *args) {
723 int defined = 1;
724 int32_t res = getOneArg(args, &defined);
725 if (!defined) fatal("sorry, DISP operand value must be known here");
726 if (res < 0 || res > 65535) fatal("invalid DISP operand value: %d", res);
727 //printf("DISP=%d\n", res);
728 disp = res;
729 return 0;
733 static int piENDDISP (const char *args) {
734 if (inTapeBlock) fatal("sorry, no ENDDISPs in TAPEDATA allowed");
735 disp = pc;
736 return 0;
740 static int piENT (const char *args) {
741 int defined = 1;
742 int32_t res = getOneArg(args, &defined);
743 if (!defined) fatal("sorry, ENT operand value must be known here");
744 if (res < 0 || res > 65535) fatal("invalid ENT operand value: %d", res);
745 ent = res;
746 return 0;
750 static int piALIGN (const char *args) {
751 int defined = 1;
752 if (inTapeBlock) fatal("sorry, no ALIGNs in TAPEDATA allowed");
753 int32_t res = getOneArg(args, &defined);
754 if (!defined) fatal("sorry, ALIGN operand value must be known here");
755 if (res < 0 || res > 65535) fatal("invalid ALIGN operand value: %d", res);
756 if (pc != disp) fatal("sorry, no ALIGNs in desynced blocks allowed");
757 if (res > 0 && pc%res != 0) {
758 pc /= res;
759 pc *= res;
760 pc += res;
761 disp = pc;
763 //printf("new DISP: 0x%04x\n", res);
764 return 0;
768 static int piDISPALIGN (const char *args) {
769 int defined = 1;
770 int32_t res = getOneArg(args, &defined);
771 if (!defined) fatal("sorry, DISPALIGN operand value must be known here");
772 if (res < 0 || res > 65535) fatal("invalid DISPALIGN operand value: %d", res);
773 if (res > 0 && disp%res != 0) {
774 disp /= res;
775 disp *= res;
776 disp += res;
778 //printf("new DISP: 0x%04x\n", res);
779 return 0;
783 static int piDEFBW (const char *args, int isWord) {
784 int32_t res;
785 int defined;
786 const char *a = args;
787 for (;;) {
788 defined = 0;
789 res = getArg(&a, &defined);
790 if (pass > 0 && !defined) fatalPos(URA_BAD_INSTRUCTION, a);
791 if (isWord) {
792 if (res < -32768 || res > 65535) fatal("invalid operand value: %d", res);
793 res = 65536+res;
794 if (isWord == 1) {
795 putByte(pc, res&0xFFU);
796 ++pc; ++disp;
797 putByte(pc, (res>>8)&0xFFU);
798 ++pc; ++disp;
799 } else {
800 putByte(pc, (res>>8)&0xFFU);
801 ++pc; ++disp;
802 putByte(pc, res&0xFFU);
803 ++pc; ++disp;
805 } else {
806 if (res < -128 || res > 255) fatal("invalid operand value: %d", res);
807 putByte(pc, res&0xFFU);
808 ++pc; ++disp;
810 if (*a) {
811 if (*a == ';') break;
812 if (*a != ',') fatalPos(URA_BAD_OPERAND, a);
813 ++a;
814 } else {
815 break;
818 return 0;
821 static int piDEFB (const char *args) { return piDEFBW(args, 0); }
822 static int piDEFW (const char *args) { return piDEFBW(args, 1); }
823 static int piDEFR (const char *args) { return piDEFBW(args, 2); }
826 static int piDEFSTR (const char *args, int type) {
827 char *res;
828 int len, f;
829 const char *a = args;
831 for (;;) {
832 if (*a == '"' || *a == '\'') {
833 res = getStrArg(&a, &len);
834 for (f = 0; f < len; ++f) {
835 unsigned char b = (unsigned char)res[f];
836 if (f == len-1 && type == 2) b |= 0x80;
837 putByte(pc, b);
838 ++pc; ++disp;
840 free(res);
841 if (type == 1) {
842 putByte(pc, (unsigned char)res[f]);
843 ++pc; ++disp;
845 } else {
846 int defined = 1;
847 int32_t v = getArg(&a, &defined);
848 if (pass > 0 && !defined) fatalPos(URA_BAD_OPERAND, a);
849 if (!defined) v = 0;
850 putByte(pc, v&0xFF);
851 ++pc; ++disp;
853 if (*a) {
854 if (*a == ';') break;
855 if (*a != ',') fatalPos(URA_BAD_OPERAND, a);
856 ++a;
857 } else {
858 break;
860 for (; *a && isspace(*a); ++a) ;
861 if (!a[0]) break;
863 return 0;
867 static int piDEFM (const char *args) { return piDEFSTR(args, 0); }
868 static int piDEFZ (const char *args) { return piDEFSTR(args, 1); }
869 static int piDEFX (const char *args) { return piDEFSTR(args, 2); }
872 static int piDEFS (const char *args) {
873 int32_t res, bt, f;
874 int defined = 0;
875 const char *a = args;
877 res = getArg(&a, &defined);
878 if (pass > 0 && !defined) fatalPos(URA_BAD_INSTRUCTION, a);
879 if (res < 1 || res > 65535) fatalPos(URA_BAD_OPERAND, a);
880 if (*a && a[0] == ',') {
881 ++a;
882 bt = getArg(&a, &defined);
883 if (pass > 0 && !defined) fatalPos(URA_BAD_INSTRUCTION, a);
884 if (bt < -127 || bt > 255) fatalPos(URA_BAD_OPERAND, a);
885 if (bt < 0) bt = 256+bt;
886 for (f = 0; f < res; ++f) {
887 putByte(pc, bt&0xFFU);
888 ++pc; ++disp;
890 } else {
891 pc += res; disp += res;
893 if (*a) {
894 if (*a != ';') fatalPos(URA_BAD_OPERAND, a);
896 return 0;
900 static int piDISPLAYA (const char *args, int anyPass) {
901 char *res;
902 int len;
903 const char *a = args;
905 for (;;) {
906 if (*a == '"' || *a == '\'') {
907 res = getStrArg(&a, &len);
908 if (anyPass || pass > 0) printf("%s", res);
909 free(res);
910 } else {
911 int defined = 1;
912 int32_t v = getArg(&a, &defined);
913 if (pass > 0 && !defined) fatalPos(URA_BAD_OPERAND, a);
914 if (anyPass || pass > 0) printf("%d", v);
916 if (*a) {
917 if (*a == ';') break;
918 if (*a != ',') fatalPos(URA_BAD_OPERAND, a);
919 ++a;
920 } else {
921 break;
923 for (; *a && isspace(*a); ++a) ;
924 if (!a[0]) break;
926 return 0;
930 static int piDISPLAY (const char *args) { return piDISPLAYA(args, 0); }
931 static int piDISPLAY0 (const char *args) { return piDISPLAYA(args, 1); }
934 /* INCBIN "name"[,maxlen] */
935 static int piINCBIN (const char *args) {
936 int system = 0;
937 const char *a = args;
938 uint8_t bt;
939 FILE *fl;
940 int maxlen = 65536;
941 char *fn = getIncludeArg(&a, &system);
943 if (system && fn[0] != '/') {
944 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
945 free(fn);
946 fn = strdup(curLine);
948 // maxlen
949 if (*a == ',') {
950 ++a;
951 int defined = 1;
952 maxlen = getOneArg(a, &defined);
953 if (!defined) {
954 errorMsg("INCBIN: undefined maxlen (%s)", fn);
955 free(fn);
956 fatal("compilation failed!");
958 if (maxlen < 1) { free(fn); return 0; }
961 fl = fopen(fn, "rb");
962 if (!fl) {
963 errorMsg("INCBIN: file not found: %s", fn);
964 free(fn);
965 fatal("compilation failed!");
967 while (maxlen-- > 0) {
968 int res = fread(&bt, 1, 1, fl);
969 if (!res) break;
970 if (res != 1) {
971 errorMsg("INCBIN: error reading file: %s", fn);
972 free(fn);
973 fatal("compilation failed!");
975 putByte(pc, bt);
976 ++pc; ++disp;
978 fclose(fl);
979 free(fn);
980 return 0;
984 static int piINCLUDE (const char *args) {
985 int oi = modInCurFile;
986 int system = 0;
987 char *fn = getOneIncludeArg(args, &system);
988 FileStack *c = malloc(sizeof(FileStack));
989 int res;
991 if (system && fn[0] != '/') {
992 sprintf(curLine, "%s/%s", sysIncludeDir, fn);
993 free(fn);
994 fn = strdup(curLine);
996 memset(c, 0, sizeof(FileStack));
997 c->fl = curFile;
998 c->name = curFileName;
999 c->lineNo = curLineNo;
1000 c->prev = fstack;
1001 fstack = c;
1002 curFileName = NULL;
1003 modInCurFile = 0;
1004 res = processFile(fn);
1005 modInCurFile = oi;
1006 popFile();
1007 free(fn);
1008 if (res) {
1009 fatal("compilation failed");
1010 //longjmp(errJP, 667);
1012 memset(curLine, 0, sizeof(curLine));
1013 return 0;
1017 static int piMODULE (const char *args) {
1018 char *mn;
1020 if (curModuleName) fatal("no nested modules allowed (yet)");
1021 mn = getOneLabelArg(args);
1022 if (!urIsValidLabelName(mn)) { free(mn); fatal("invalid module name"); }
1023 free(curModuleName);
1024 curModuleName = mn;
1025 moduleSkipFlag = isKnownModule(mn);
1026 addKnownModule(mn);
1027 modInCurFile = 1;
1028 return 0;
1032 static int piENDMODULE (const char *args) {
1033 char *mn;
1035 if (!curModuleName) fatal("ENDMODULE without MODULE");
1036 if (*args && args[0] != ';') {
1037 mn = getOneLabelArg(args);
1038 if (strcmp(mn, curModuleName)) { free(mn); fatal("invalid module name in ENDMODULE"); }
1039 free(mn);
1041 free(curModuleName);
1042 curModuleName = NULL;
1043 moduleSkipFlag = 0;
1044 modInCurFile = 0;
1045 return 0;
1049 ///////////////////////////////////////////////////////////////////////////////
1050 // tape fun
1052 // TAPEBLOCK name,start
1053 static int piTAPEBLOCK (const char *args) {
1054 char *sres;
1055 int32_t ires;
1056 char name[11];
1057 int defined, f, len;
1058 const char *a = args;
1059 uint16_t start;
1061 if (inTapeBlock) fatal("sorry, no TAPEHEADERs allowed in TAPEDATA!");
1062 memset(name, 32, sizeof(name));
1063 name[10] = '\0';
1064 // get name
1065 sres = getStrArg(&a, &len);
1066 if (len > 0) {
1067 if (len > 10) len = 10;
1068 memmove(name, sres, len);
1070 if (*a != ',') fatalPos(URA_BAD_OPERAND, a); ++a;
1071 // get start
1072 ires = getArg(&a, &defined);
1073 if (pass > 0 && !defined) fatalPos(URA_BAD_OPERAND, a);
1074 if (ires < 0 || ires > 65535) fatal("TAPEHEADER: bad start: %d", ires);
1075 start = ires;
1076 if (*a && *a != ';') fatalPos(URA_BAD_OPERAND, a);
1077 // now write header
1078 //if (pass > 0) printf("%04X(%04X): TAPE HEADER: [%s], len=%u, start=%u, len2=%u\n", pc, disp, name, length, start, length2);
1079 putWord(pc, 19); pc += 2; // length of header
1080 tapeBlockHdrAddr = pc;
1081 inTapeBlock = 1; tapeXorB = 0; // prepare state
1082 putByte(pc, 0); ++pc; // header block
1083 putByte(pc, 3); ++pc;
1084 for (f = 0; f < 10; ++f, ++pc) putByte(pc, name[f]);
1085 putWord(pc, 0); pc += 2;
1086 putWord(pc, start); pc += 2;
1087 putWord(pc, 32767); pc += 2;
1088 inTapeBlock = 0;
1089 putByte(pc, tapeXorB); ++pc;
1090 // header written
1091 tapeBlockSizeAddr = pc;
1092 putWord(pc, 0); pc += 2; // this will be patched in ENDTAPEDATA
1093 inTapeBlock = 1; tapeXorB = 0; // prepare state
1094 putByte(pc, 255); ++pc; // data block
1095 disp = start;
1096 return 0;
1100 static int piENDTAPEBLOCK (const char *args) {
1101 int sz, f;
1102 if (!inTapeBlock) fatal("sorry, no ENDTAPEBLOCK allowed without TAPEBLOCK!");
1103 inTapeBlock = 0;
1104 putByte(pc, tapeXorB); ++pc;
1105 //if (pass > 0) printf("%04X(%04X): TAPE BLOCK END\n", pc, disp);
1106 // patch size
1107 sz = pc; if (sz == 0) sz = 65536;
1108 sz -= (int)tapeBlockSizeAddr+2;
1109 putWord(tapeBlockSizeAddr, sz);
1110 // patch header
1111 putWord(tapeBlockHdrAddr+12, sz-2); // length patched
1112 // calc checksum
1113 tapeXorB = 0;
1114 for (f = 0; f < 18; ++f) tapeXorB ^= getByte(tapeBlockHdrAddr+f);
1115 putByte(tapeBlockHdrAddr+18, tapeXorB); // checksum patched
1117 return 0;
1121 // TAPEHEADER type,name,length,start[,lenwithoutvars]
1122 // TAPEDATA
1123 // ENDTAPEDATA
1124 static int piTAPEHEADER (const char *args) {
1125 char *sres;
1126 int32_t ires;
1127 char name[11];
1128 int defined, f, len;
1129 const char *a = args;
1130 uint8_t type;
1131 uint16_t length, start, length2;
1133 if (inTapeBlock) fatal("sorry, no TAPEHEADERs allowed in TAPEDATA!");
1134 memset(name, 32, sizeof(name));
1135 name[10] = '\0';
1136 // get type
1137 ires = getArg(&a, &defined);
1138 if (!defined) fatalPos(URA_BAD_OPERAND, a);
1139 if (ires < 0 || ires > 255) fatal("TAPEHEADER: bad type: %d", ires);
1140 type = ires;
1141 if (*a != ',') fatalPos(URA_BAD_OPERAND, a); ++a;
1142 // get name
1143 sres = getStrArg(&a, &len);
1144 if (len > 0) {
1145 if (len > 10) len = 10;
1146 memmove(name, sres, len);
1148 if (*a != ',') fatalPos(URA_BAD_OPERAND, a); ++a;
1149 // get length
1150 ires = getArg(&a, &defined);
1151 if (pass > 0 && !defined) fatalPos(URA_BAD_OPERAND, a);
1152 if (ires < 0 || ires > 65535) fatal("TAPEHEADER: bad length: %d", ires);
1153 length2 = length = ires;
1154 if (*a != ',') fatalPos(URA_BAD_OPERAND, a); ++a;
1155 // get start
1156 ires = getArg(&a, &defined);
1157 if (pass > 0 && !defined) fatalPos(URA_BAD_OPERAND, a);
1158 if (ires < 0 || ires > 65535) fatal("TAPEHEADER: bad start: %d", ires);
1159 start = ires;
1160 // get length2
1161 if (*a == ',') {
1162 ires = getArg(&a, &defined);
1163 if (pass > 0 && !defined) fatalPos(URA_BAD_OPERAND, a);
1164 if (ires < 0 || ires > 65535) fatal("TAPEHEADER: bad length2: %d", ires);
1165 length2 = ires;
1167 if (*a && *a != ';') fatalPos(URA_BAD_OPERAND, a);
1168 // now write header
1169 //if (pass > 0) printf("%04X(%04X): TAPE HEADER: [%s], len=%u, start=%u, len2=%u\n", pc, disp, name, length, start, length2);
1170 putWord(pc, 19); pc += 2; // length of header
1171 inTapeBlock = 1; tapeXorB = 0; // prepare state
1172 putByte(pc, 0); ++pc; // header block
1173 putByte(pc, type); ++pc;
1174 for (f = 0; f < 10; ++f, ++pc) putByte(pc, name[f]);
1175 putWord(pc, length); pc += 2;
1176 putWord(pc, start); pc += 2;
1177 putWord(pc, length2); pc += 2;
1178 inTapeBlock = 0;
1179 putByte(pc, tapeXorB); ++pc;
1180 // header written
1181 return 0;
1185 static int piTAPEDATA (const char *args) {
1186 if (inTapeBlock) fatal("sorry, no TAPEDATAs allowed in TAPEDATA!");
1187 //if (pass > 0) printf("%04X(%04X): TAPE BLOCK\n", pc, disp);
1188 tapeBlockSizeAddr = pc;
1189 putWord(pc, 0); pc += 2; // this will be patched in ENDTAPEDATA
1190 inTapeBlock = 1; tapeXorB = 0; // prepare state
1191 putByte(pc, 255); ++pc; // data block
1192 return 0;
1196 static int piENDTAPEDATA (const char *args) {
1197 int sz;
1198 if (!inTapeBlock) fatal("sorry, no ENDTAPEDATA allowed without TAPEDATA!");
1199 inTapeBlock = 0;
1200 putByte(pc, tapeXorB); ++pc;
1201 //if (pass > 0) printf("%04X(%04X): TAPE BLOCK END\n", pc, disp);
1202 // patch size
1203 sz = pc;
1204 sz -= (int)tapeBlockSizeAddr+2;
1205 putByte(tapeBlockSizeAddr, sz&0xFF);
1206 putByte(tapeBlockSizeAddr+1, (sz>>8)&0xFF);
1207 return 0;
1211 ///////////////////////////////////////////////////////////////////////////////
1212 // duplicator
1214 static int piDUP (const char *args) {
1215 int32_t res;
1216 int defined = 0;
1217 DupChunk *cdup = curDupChunk();
1219 res = getOneArg(args, &defined);
1220 if (!defined) fatal("DUP operand must be defined!");
1221 if (res > 65535) fatal("invalid count in DUP: %d", res);
1222 if (cdup && cdup->count < 1) res = 0; // just skip it
1223 newDupChunk(res);
1224 return 0;
1228 static int piEDUP (const char *args) {
1229 DupChunk *cdup = curDupChunk();
1230 if (!cdup) fatal("EDUP withoud DUP");
1231 if (--(cdup->count) < 1) {
1232 /* the end */
1233 dropDupChunk();
1234 } else {
1235 /* do it all again */
1236 fseek(curFile, cdup->fpos, SEEK_SET);
1237 curLineNo = cdup->lineNo;
1239 return 0;
1243 ///////////////////////////////////////////////////////////////////////////////
1244 // instruction dispatcher
1246 static int processInstruction (void) {
1247 char instr[64];
1248 UrAsmOp *op;
1249 char *args = getOpName(instr, curLine);
1250 if (!args) return -1;
1251 op = urFindOp(instr);
1252 if (op) {
1253 if (op->fn(args)) return -2;
1254 return 0;
1256 return -1;
1260 ///////////////////////////////////////////////////////////////////////////////
1261 // Iptscrae
1263 static IptEngine eng;
1264 static int scriptStartLine = 0;
1267 //TODO
1268 static int iptCheckAbort (IptEngine *eng) {
1269 return 0;
1273 void iptParseError (IptParserState *pst, const char *msg) {
1274 fprintf(stderr, "PARSE ERROR at line %d: %s\n", scriptStartLine+pst->line-1, msg);
1278 void iptError (IptEngine *eng, IptCell errCell, const char *msg) {
1279 fprintf(stderr, "SCRIPT ERROR at file %s, line %d\n", curFileName, scriptStartLine);
1280 if (errCell) { fprintf(stderr, " "); iptDebugPrintCell(eng, errCell); fprintf(stderr, "\n"); }
1281 fatal("SCRIPT ERROR!");
1285 static __attribute__((unused)) void dumpStack (IptEngine *eng) {
1286 uint32_t f;
1287 if (eng->sp) {
1288 fprintf(stderr, "stack depth: %u\n", iptStackDepth(eng));
1289 for (f = eng->sp; f > 0; f--) {
1290 fprintf(stderr, " #%u: ", eng->sp-f);
1291 iptDebugPrintCell(eng, eng->stack[f-1]);
1292 fputs("\n", stderr);
1298 static void runScript (IptCell prg) {
1299 if (prg) {
1300 iptExecute(&eng, prg);
1301 //dumpStack(&eng);
1306 static int doLine (void) {
1307 int len;
1308 const char *errpos;
1309 DupChunk *cdup;
1311 cdup = curDupChunk();
1312 if (curLine[0] == ';') return 0;
1313 if (curLine[0] == ':') fatal("line can't start with colon!");
1314 if (curLine[0] == '*') fatal("line can't start with star!");
1315 if (!isspace(curLine[0])) {
1316 if (processLabel()) return 0;
1318 again:
1319 trimright(curLine);
1320 if (!cdup || cdup->count > 0) {
1321 len = urAssembleOne(curLine, pc, disp, &errpos);
1322 if (len < 0) {
1323 if (processInstruction()) fatalPos(len, errpos);
1324 return 0;
1326 if (len > 0 && pass != 0 && optShowOut) {
1327 char dstr[256], *p;
1328 int ppp = pc, ll = 0, f;
1329 for (p = curLine; *p && isspace(*p); ++p) ;
1330 printf("** %s (len=%d)\n", p, len);
1331 while (ll < len) {
1332 int len1 = urDisassembleOne(dstr, ppp);
1333 printf("** %s\n%04X: [", dstr, (unsigned int)ppp);
1334 for (f = 0; f < len1; ++f) { if (f) putchar(' '); printf("%02X", memory[ppp+f]); }
1335 printf("] (len1=%d)\n", len1);
1336 ppp += len1; ll += len1;
1339 pc += len; disp += len;
1340 if (len >= 0 && errpos) {
1341 memmove(curLine, errpos+1, strlen(errpos));
1342 goto again;
1345 return 0;
1349 ///////////////////////////////////////////////////////////////////////////////
1350 // Iptscrae primitives
1352 /* "string" SAY */
1353 static int iptPrimSAY (IptEngine *eng) {
1354 IptCell res = iptGetValue(eng, iptPop(eng));
1355 switch (res->type) {
1356 case IptCellInt:
1357 printf("%i\n", res->iv);
1358 break;
1359 case IptCellFloat:
1360 printf("%.15g\n", res->fv);
1361 break;
1362 case IptCellString:
1363 printf("%s\n", res->sv);
1364 break;
1365 default:
1366 printf("<bad cell type> (%d)\n", res->type);
1367 break;
1369 return 0;
1373 /* GETPASS */
1374 static int iptPrimGETPASS (IptEngine *eng) { iptPushInt(eng, pass); return 0; }
1375 /* GETPC */
1376 static int iptPrimGETPC (IptEngine *eng) { iptPushInt(eng, pc); return 0; }
1377 /* GETDISP */
1378 static int iptPrimGETDISP (IptEngine *eng) { iptPushInt(eng, disp); return 0; }
1381 /* val SETPC */
1382 static int iptPrimSETPC (IptEngine *eng) {
1383 int32_t a = iptPopInt(eng);
1384 pc = a&0xFFFF;
1385 return 0;
1389 /* val SETDISP */
1390 static int iptPrimSETDISP (IptEngine *eng) {
1391 int32_t a = iptPopInt(eng);
1392 disp = a&0xFFFF;
1393 return 0;
1397 /* addr GETBYTE */
1398 static int iptPrimGETBYTE (IptEngine *eng) {
1399 int32_t a = iptPopInt(eng);
1400 iptPushInt(eng, getByte(a&0xFFFF));
1401 return 0;
1405 /* val addr PUTBYTE */
1406 static int iptPrimPUTBYTE (IptEngine *eng) {
1407 int32_t a = iptPopInt(eng);
1408 int32_t v = iptPopInt(eng);
1409 putByte(a&0xFFFF, v&0xFF);
1410 return 0;
1414 /* val EMITBYTE */
1415 static int iptPrimEMITBYTE (IptEngine *eng) {
1416 int32_t v = iptPopInt(eng);
1417 putByte(pc, v&0xFF);
1418 ++disp; ++pc;
1419 return 0;
1423 /* "str" ASSEMBLE */
1424 static int iptPrimASSEMBLE (IptEngine *eng) {
1425 const char *str = iptPopString(eng);
1426 if (strlen(str) >= MAX_LINE_SIZE) fatal("line too long!\n%s", str);
1427 strcpy(curLine, str);
1428 doLine();
1429 return 0;
1433 /* "str" EXPRESSION */
1434 static int iptPrimEXPRESSION (IptEngine *eng) {
1435 const char *str = iptPopString(eng);
1436 int error, defined;
1437 int32_t res;
1438 const char *ee;
1440 ee = urExpression(&res, str, disp, &defined, &error);
1441 if (error || (*ee && ee[0] != ';' && ee[0] != ',')) fatal("invalid expression: %s", str);
1442 iptPushInt(eng, res);
1443 iptPushInt(eng, defined);
1444 return 0;
1449 "name" FINDLABEL
1450 returns:
1451 0: no such label
1452 -1: label known, but undefined
1453 1: label known and defined
1455 static int iptPrimFINDLABEL (IptEngine *eng) {
1456 const char *str = iptPopString(eng);
1457 UrLabelInfo *lbl = urFindLabel(str);
1458 if (!lbl) iptPushInt(eng, 0);
1459 else iptPushInt(eng, lbl->type>=0 && lbl->known ? 1 : -1);
1460 return 0;
1465 "name" LABELTYPE
1466 returns:
1467 0: no such label
1468 1: '='
1469 2: EQU
1470 3: code label
1472 static int iptPrimLABELTYPE (IptEngine *eng) {
1473 const char *str = iptPopString(eng);
1474 UrLabelInfo *lbl = urFindLabel(str);
1475 if (!lbl) iptPushInt(eng, 0);
1476 else iptPushInt(eng, lbl->type>=0 ? lbl->type+1 : 0);
1477 return 0;
1482 * "str" NEXTARG
1483 * returns:
1484 * reststr value 1/-1 (value can be string or int; -1: undefined)
1487 static int iptPrimNEXTARG (IptEngine *eng) {
1488 IptCell cell = iptPopValue(eng);
1489 if (cell->type != IptCellString) iptError(eng, cell, "string expected");
1490 const char *str = cell->sv;
1492 while (*str && isspace(*str)) ++str;
1493 if (!str[0] || str[0] == ';') {
1494 // no more
1495 iptPushInt(eng, 0);
1496 return 0;
1498 if (str[0] == '"' || str[0] == '\'') {
1499 // string
1500 int len;
1501 char *ee = getStrArg(&str, &len);
1502 iptNewStringCellEx(eng, str, strlen(str), cell);
1503 iptNewStringCellEx(eng, ee, len, cell);
1504 free(ee);
1505 iptPushInt(eng, 1);
1506 } else {
1507 // math
1508 int defined = 1;
1509 int32_t v = getArg(&str, &defined);
1510 iptNewStringCellEx(eng, str, strlen(str), cell);
1511 iptPushInt(eng, v);
1512 iptPushInt(eng, defined?1:-1);
1514 return 0;
1518 ///////////////////////////////////////////////////////////////////////////////
1519 // scripted macros
1521 typedef struct ScriptMacroInfo ScriptMacroInfo;
1522 struct ScriptMacroInfo {
1523 char *name;
1524 IptCell code; /* ( "strarg" -- ) */
1525 ScriptMacroInfo *next;
1528 static ScriptMacroInfo *smi = NULL;
1531 static void smiClear (void) {
1532 while (smi) {
1533 ScriptMacroInfo *c = smi;
1534 smi = smi->next;
1535 free(c->name);
1536 free(c);
1541 static void smiMark (IptEngine *eng) {
1542 ScriptMacroInfo *c;
1543 for (c = smi; c; c = c->next) iptMark(eng, c->code);
1547 static ScriptMacroInfo *smiFind (const char *name) {
1548 ScriptMacroInfo *c;
1549 for (c = smi; c; c = c->next) if (!strcasecmp(name, c->name)) return c;
1550 return NULL;
1554 static void smiAdd (const char *name, IptCell cell) {
1555 ScriptMacroInfo *c = malloc(sizeof(ScriptMacroInfo));
1556 memset(c, 0, sizeof(ScriptMacroInfo));
1557 c->name = strdup(name);
1558 c->code = cell;
1559 c->next = smi;
1560 smi = c;
1565 * {code} "str" DEFMAC
1567 static int iptPrimDEFMAC (IptEngine *eng) {
1568 const char *name = iptPopString(eng);
1569 IptCell code = iptPopValue(eng);
1570 if (code->type != IptCellList) iptError(eng, code, "code expected");
1571 if (pass != 0) return 0; // do nothing
1572 if (!name[0]) iptError(eng, code, "DEFMAC empty name");
1573 // zeroth pass, register it
1574 if (smiFind(name)) iptError(eng, code, "DEFMAC double defined");
1575 smiAdd(name, code);
1576 return 0;
1580 ///////////////////////////////////////////////////////////////////////////////
1581 // file compiler
1583 static int processFile (const char *fname) {
1584 int len;
1585 const char *errpos;
1586 jmp_buf oJP;
1587 DupChunk *cdup;
1588 char instr[64];
1589 const char *args;
1590 char *script = NULL;
1591 int scriptPass = -1;
1593 if (curFileName) free(curFileName);
1594 curFileName = strdup(fname);
1595 curLineNo = 0;
1596 if (!(curFile = fopen(fname, "r"))) {
1597 free(curFileName); curFileName = NULL;
1598 fprintf(stderr, "ERROR: can't open input file %s!\n", fname);
1599 return -1;
1602 memmove(&oJP, &errJP, sizeof(jmp_buf));
1603 if (setjmp(errJP)) {
1604 free(curFileName); curFileName = NULL;
1605 fclose(curFile); curFile = NULL;
1606 if (script) free(script);
1607 memmove(&errJP, &oJP, sizeof(jmp_buf));
1608 return -1;
1611 for (;;) {
1612 if (feederCB) {
1613 if (feederCB()) continue;
1614 } else {
1615 if (!curFile) break;
1616 if (!fgets(curLine, sizeof(curLine)-1, curFile)) break;
1617 ++curLineNo;
1618 curLine[sizeof(curLine)-1] = '\0';
1619 trimright(curLine);
1620 //printf("%s: [%s]\n", curFileName, curLine);
1622 if (collectorCB) {
1623 collectorCB();
1624 continue;
1626 if (script) {
1627 /* collecting script */
1628 if (strncasecmp(curLine, "*ENDSCRIPT", 10)) {
1629 /* still collecting */
1630 int len = strlen(script), l1 = strlen(curLine);
1631 char *nn = realloc(script, len+l1+2); // zero and '\n'
1632 if (!nn) fatal("out of memory for script");
1633 script = nn;
1634 strcat(script, curLine);
1635 strcat(script, "\n");
1636 continue;
1637 } else {
1638 /* collected */
1639 cdup = curDupChunk();
1640 if (!cdup || cdup->count > 0) {
1641 /* execute */
1642 if (scriptPass == -1 || scriptPass == pass) {
1643 IptCell prg = iptParse(&eng, script);
1644 if (prg) runScript(prg);
1645 iptClearStack(&eng);
1648 free(script); script = NULL;
1649 continue;
1652 // check for dups
1653 if (!curLine[0]) continue;
1654 if (curLine[0] == ';') continue;
1655 if (curLine[0] == ':') fatal("line can't start with colon!");
1656 if (moduleSkipFlag) {
1657 if (!isspace(curLine[0])) continue;
1658 if ((args = getOpName(instr, curLine)) == NULL) continue;
1659 if (!strcasecmp(instr, "ENDMODULE")) { piENDMODULE(args); continue; }
1660 continue;
1662 if (curLine[0] != '*' && !isspace(curLine[0])) {
1663 if (processLabel()) continue;
1665 cdup = curDupChunk();
1666 if (curLine[0] == '*') {
1667 /* this should be SCRIPT part */
1668 if ((args = getOpName(instr, curLine+1)) != NULL) {
1669 if (!strcasecmp(instr, "SCRIPT") || !strcasecmp(instr, "SCRIPT0") || !strcasecmp(instr, "SCRIPT1")) {
1670 /* collect it */
1671 switch (instr[strlen(instr)-1]) {
1672 case '0': scriptPass = 0; break;
1673 case '1': scriptPass = 1; break;
1674 default: scriptPass = -1; break;
1676 script = malloc(1);
1677 script[0] = '\0';
1678 scriptStartLine = curLineNo;
1679 continue;
1683 again:
1684 trimright(curLine);
1685 if (!cdup || cdup->count > 0) {
1686 len = urAssembleOne(curLine, pc, disp, &errpos);
1687 if (len < 0) {
1688 if (processInstruction()) fatalPos(len, errpos);
1689 continue;
1691 if (len > 0 && pass != 0 && optShowOut) {
1692 char dstr[256], *p;
1693 int ppp = pc, ll = 0, f;
1694 for (p = curLine; *p && isspace(*p); ++p) ;
1695 printf("** %s (len=%d)\n", p, len);
1696 while (ll < len) {
1697 int len1 = urDisassembleOne(dstr, ppp);
1698 printf("** %s\n%04X: [", dstr, (unsigned int)ppp);
1699 for (f = 0; f < len1; ++f) { if (f) putchar(' '); printf("%02X", memory[ppp+f]); }
1700 printf("] (len1=%d)\n", len1);
1701 ppp += len1; ll += len1;
1704 pc += len; disp += len;
1705 if (len >= 0 && errpos) {
1706 memmove(curLine, errpos+1, strlen(errpos));
1707 goto again;
1709 } else {
1710 if ((args = getOpName(instr, curLine)) == NULL) continue;
1711 if (!strcasecmp(instr, "DUP")) { piDUP(args); continue; }
1712 if (!strcasecmp(instr, "EDUP")) { piEDUP(args); continue; }
1713 continue;
1716 if (curModuleName && modInCurFile) {
1717 fprintf(stderr, "WARNING: seems that you forgot 'ENDMODULE %s' in %s!\n", curModuleName, curFileName);
1719 if (script) free(script);
1720 if (curFile) fclose(curFile);
1721 free(curFileName); curFileName = NULL;
1722 memmove(&errJP, &oJP, sizeof(jmp_buf));
1723 return 0;
1727 ///////////////////////////////////////////////////////////////////////////////
1728 // code saver
1730 /* return 'found' flag */
1731 static int findChunkFrom (int addr, int *start, int *len) {
1732 if (addr < 0) addr = 0;
1733 for (; addr <= 65535 && !memused[addr]; ++addr) ;
1734 if (addr > 65535) return 0;
1735 *start = addr;
1736 for (; addr <= 65535 && memused[addr]; ++addr) ;
1737 *len = addr-(*start);
1738 return 1;
1742 static void saveBinChunks (const char *basename) {
1743 char *fname = malloc(strlen(basename)+16);
1744 int start = 0, len;
1745 FILE *fo;
1747 while (findChunkFrom(start, &start, &len)) {
1748 sprintf(fname, "%s_%04X.%s", basename, start, optTapExt?"tap":"bin");
1749 fo = fopen(fname, "wb");
1750 if (!fo) {
1751 fprintf(stderr, "ERROR: can't write file %s!\n", fname);
1752 } else {
1753 printf("out: %s\n", fname);
1754 if (fwrite(memory+start, len, 1, fo) != 1) {
1755 fprintf(stderr, "ERROR: error writing file %s!\n", fname);
1756 fclose(fo);
1757 unlink(fname);
1758 } else {
1759 fclose(fo);
1762 start += len;
1764 free(fname);
1768 static int fWriteByte (FILE *fo, uint8_t b, uint8_t *bxor) {
1769 if (fwrite(&b, 1, 1, fo) != 1) return -1;
1770 if (bxor) *bxor = (*bxor)^b;
1771 return 0;
1775 static int fWriteWord (FILE *fo, uint16_t w, uint8_t *bxor) {
1776 if (fWriteByte(fo, w&0xFFU, bxor)) return -1;
1777 if (fWriteByte(fo, (w>>8)&0xFFU, bxor)) return -1;
1778 return 0;
1782 static void saveTap (const char *basename) {
1783 char *fname = malloc(strlen(basename)+16);
1784 char blkname[128];
1785 int start = 0, len, f;
1786 uint8_t bxor;
1787 FILE *fo;
1789 sprintf(fname, "%s.tap", basename);
1790 fo = fopen(fname, "wb");
1791 free(fname);
1792 if (!fo) { fprintf(stderr, "ERROR: can't write file %s.tap!\n", basename); return; }
1793 while (findChunkFrom(start, &start, &len)) {
1794 // write header
1795 sprintf(blkname, "c%04X:%04X", start, len);
1796 printf(" block: %s\n", blkname);
1797 fWriteWord(fo, 19, NULL); // length of header
1798 bxor = 0;
1799 fWriteByte(fo, 0, &bxor); // header block
1800 fWriteByte(fo, 3, &bxor); // 'code' flag
1801 for (f = 0; f < 10; ++f) fWriteByte(fo, blkname[f], &bxor);
1802 fWriteWord(fo, len, &bxor);
1803 fWriteWord(fo, start, &bxor);
1804 fWriteWord(fo, 32768, &bxor);
1805 fWriteByte(fo, bxor, NULL);
1806 // write data
1807 fWriteWord(fo, len+2, NULL); // plus type and checkbyte
1808 bxor = 0;
1809 fWriteByte(fo, 0xFFU, &bxor); // data block
1810 for (f = 0; f < len; ++f) fWriteByte(fo, memory[start+f], &bxor);
1811 fWriteByte(fo, bxor, NULL);
1812 start += len;
1817 ///////////////////////////////////////////////////////////////////////////////
1818 // init dirs
1820 static void initInclideDir (void) {
1821 const char *id = getenv("URASM_INCLUDE_DIR");
1822 if (id && id[0]) {
1823 sysIncludeDir = strdup(id);
1824 } else {
1825 char buf[128], myDir[4096];
1826 pid_t pid = getpid();
1827 sprintf(buf, "/proc/%u/exe", (unsigned int)pid);
1828 memset(myDir, 0, sizeof(myDir));
1829 if (readlink(buf, myDir, sizeof(myDir)-1) < 0) strcpy(myDir, ".");
1830 else {
1831 char *p = (char *)strrchr(myDir, '/');
1832 if (!p) strcpy(myDir, "."); else *p = '\0';
1834 strcat(myDir, "/libs");
1835 sysIncludeDir = strdup(myDir);
1837 while (sysIncludeDir[0] && sysIncludeDir[strlen(sysIncludeDir)-1] == '/') sysIncludeDir[strlen(sysIncludeDir)-1] = '\0';
1838 if (!sysIncludeDir[0]) strcpy(sysIncludeDir, ".");
1842 ///////////////////////////////////////////////////////////////////////////////
1843 // main part
1845 enum {
1846 WRITE_NONE = 0,
1847 WRITE_RAW,
1848 WRITE_SNA48,
1849 WRITE_TAP
1853 static void usage (void) {
1854 printf(
1855 "usage: urasm [options] infile\n"
1856 "options:\n"
1857 " -s -sna --sna write 48K .SNA file\n"
1858 " -S -SNA --SNA write 48K .SNA file with autostart\n"
1859 " -t -tap --tap write .tap file\n"
1860 " -T -TAP --TAP write raw file(s) -- for use with TAPE instructions\n"
1861 " -r -raw --raw write raw file(s)\n"
1862 " -b -bin --bin write raw file(s) -- same as '-r'\n"
1863 "\n"
1864 " -n -none --none write nothing\n"
1869 int main (int argc, char *argv[]) {
1870 int res;
1871 const char *infile = NULL;
1872 static int optWriteType = WRITE_SNA48;
1874 printf("urasm, %s %s\n", __DATE__, __TIME__);
1875 while (argc > 1 && argv[1][0] == '-') {
1876 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-help")) {
1877 usage();
1878 return 1;
1880 if (!strcmp(argv[1], "-s") || !strcmp(argv[1], "--sna") || !strcmp(argv[1], "-sna")) {
1881 optWriteType = WRITE_SNA48;
1882 optRunSNA = 0;
1883 optTapExt = 0;
1884 --argc; ++argv;
1885 continue;
1887 if (!strcmp(argv[1], "-S") || !strcmp(argv[1], "--SNA") || !strcmp(argv[1], "-SNA")) {
1888 optWriteType = WRITE_SNA48;
1889 optRunSNA = 1;
1890 optTapExt = 0;
1891 --argc; ++argv;
1892 continue;
1894 if (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--tap") || !strcmp(argv[1], "-tap")) {
1895 optWriteType = WRITE_TAP;
1896 optTapExt = 0;
1897 --argc; ++argv;
1898 continue;
1900 if (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--raw") || !strcmp(argv[1], "-raw")) {
1901 optWriteType = WRITE_RAW;
1902 optTapExt = 0;
1903 --argc; ++argv;
1904 continue;
1906 if (!strcmp(argv[1], "-T") || !strcmp(argv[1], "--TAP") || !strcmp(argv[1], "-TAP")) {
1907 optWriteType = WRITE_RAW;
1908 optTapExt = 1;
1909 --argc; ++argv;
1910 continue;
1912 if (!strcmp(argv[1], "-b") || !strcmp(argv[1], "--bin") || !strcmp(argv[1], "-bin")) {
1913 optWriteType = WRITE_RAW;
1914 optTapExt = 0;
1915 --argc; ++argv;
1916 continue;
1918 if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--none") || !strcmp(argv[1], "-none")) {
1919 optWriteType = WRITE_NONE;
1920 --argc; ++argv;
1921 continue;
1923 fprintf(stderr, "ERROR: invalid option: %s\n", argv[1]);
1924 usage();
1925 return 1;
1928 if (argc > 1) {
1929 infile = argv[1];
1930 } else {
1931 static const char *names[] = {"main.zas", "main.a80", "main.asm", NULL};
1932 int f;
1933 for (f = 0; names[f]; ++f) {
1934 FILE *fl = fopen(names[f], "r");
1935 if (fl) {
1936 infile = names[f];
1937 fclose(fl);
1938 break;
1941 if (!infile) {
1942 fprintf(stderr, "ERROR: no file name specified!\n");
1943 return 1;
1947 initInclideDir();
1949 iptInit(&eng);
1950 iptSetAbortCheckFn(&eng, iptCheckAbort);
1951 iptSetMarkFn(&eng, smiMark);
1953 iptAddPrim(&eng, "SAY", iptPrimSAY);
1954 iptAddPrim(&eng, "GETPASS", iptPrimGETPASS);
1955 iptAddPrim(&eng, "GETPC", iptPrimGETPC);
1956 iptAddPrim(&eng, "GETDISP", iptPrimGETDISP);
1957 iptAddPrim(&eng, "SETPC", iptPrimSETPC);
1958 iptAddPrim(&eng, "SETDISP", iptPrimSETDISP);
1959 iptAddPrim(&eng, "GETBYTE", iptPrimGETBYTE);
1960 iptAddPrim(&eng, "PUTBYTE", iptPrimPUTBYTE);
1961 iptAddPrim(&eng, "EMITBYTE", iptPrimEMITBYTE);
1962 iptAddPrim(&eng, "ASSEMBLE", iptPrimASSEMBLE);
1963 iptAddPrim(&eng, "EXPRESSION", iptPrimEXPRESSION);
1964 iptAddPrim(&eng, "FINDLABEL", iptPrimFINDLABEL);
1965 iptAddPrim(&eng, "LABELTYPE", iptPrimLABELTYPE);
1966 iptAddPrim(&eng, "NEXTARG", iptPrimNEXTARG);
1967 iptAddPrim(&eng, "DEFMAC", iptPrimDEFMAC);
1968 //iptAddPrim(&eng, "NEWLABEL", iptPrimNEWLABEL);
1969 //iptAddPrim(&eng, "LOCALIZELABEL", iptPrimLOCALIZELABEL);
1970 //iptAddPrim(&eng, "GETCURMODULE", iptPrimGETCURMODULE);
1972 urGetByte = getByte;
1973 urPutByte = putByte;
1974 urFindLabelByName = findLabelCB;
1976 urAddOp("TAPEHEADER", piTAPEHEADER);
1977 urAddOp("TAPEDATA", piTAPEDATA);
1978 urAddOp("ENDTAPEDATA", piENDTAPEDATA);
1979 urAddOp("TAPEBLOCK", piTAPEBLOCK);
1980 urAddOp("ENDTAPEBLOCK", piENDTAPEBLOCK);
1982 urAddOp("DISPLAY", piDISPLAY);
1983 urAddOp("DISPLAY0", piDISPLAY0);
1984 urAddOp("MODULE", piMODULE);
1985 urAddOp("ENDMODULE", piENDMODULE);
1986 urAddOp("INCLUDE", piINCLUDE);
1987 urAddOp("INCBIN", piINCBIN);
1988 urAddOp("ENT", piENT);
1989 urAddOp("ORG", piORG);
1990 urAddOp("DISP", piDISP);
1991 urAddOp("ENDDISP", piENDDISP);
1992 urAddOp("ALIGN", piALIGN);
1993 urAddOp("DISPALIGN", piDISPALIGN);
1994 urAddOp("DUP", piDUP);
1995 urAddOp("EDUP", piEDUP);
1996 urAddOp("DEFB", piDEFB);
1997 urAddOp("DB", piDEFB);
1998 urAddOp("DEFW", piDEFW);
1999 urAddOp("DW", piDEFW);
2000 urAddOp("DEFR", piDEFR);
2001 urAddOp("DR", piDEFR);
2002 urAddOp("DEFS", piDEFS);
2003 urAddOp("DS", piDEFS);
2004 urAddOp("DEFM", piDEFM);
2005 urAddOp("DM", piDEFM);
2006 urAddOp("DEFZ", piDEFZ);
2007 urAddOp("DZ", piDEFZ);
2008 urAddOp("DEFX", piDEFX);
2009 urAddOp("DX", piDEFX);
2011 if (argc > 2) optShowOut = 1;
2012 for (pass = 0; pass <= 1; ++pass) {
2013 printf("pass %d...\n", pass);
2014 pc = disp = ent = 0x100;
2015 wasOrg = 0;
2016 inTapeBlock = 0;
2017 tapeXorB = 0;
2018 modInCurFile = 0;
2019 prepareMemory(optWriteType==WRITE_SNA48);
2020 res = processFile(infile);
2021 if (res) goto quit;
2022 if (checkLabels()) goto quit;
2023 checkUndefineds();
2024 clearKnownModules();
2025 if (tapeBlockName) free(tapeBlockName); tapeBlockName = NULL;
2026 if (lastSeenLabel) free(lastSeenLabel); lastSeenLabel = NULL;
2027 if (curModuleName) free(curModuleName); curModuleName = NULL;
2031 char *oc = strdup(infile);
2032 char *ps = strchr(oc, '/');
2033 char *pd = strchr(oc, '.');
2034 if (pd && (!ps || pd > ps)) *pd = '\0';
2035 switch (optWriteType) {
2036 case WRITE_RAW: saveBinChunks(oc); break;
2037 case WRITE_TAP: saveTap(oc); break;
2038 case WRITE_SNA48: writeSNA(oc); break;
2039 default: ;
2041 free(oc);
2044 quit:
2045 if (tapeBlockName) free(tapeBlockName);
2046 if (sysIncludeDir) free(sysIncludeDir);
2047 if (lastSeenLabel) free(lastSeenLabel);
2048 if (curModuleName) free(curModuleName);
2049 clearFileStack();
2050 clearKnownModules();
2051 urClearLabels();
2052 urClearOps();
2053 iptDeinit(&eng);
2054 return res?1:0;