BSR and RST now takes infinite number of arguments and pushes 'em in pascal style
[awish.git] / src / awasm.c
blob2f7be2447ab57cfe0487e9cfc02854b73ab92d5d
1 /*
2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 /* WARNING! HERE BE DRAGONS! */
16 #include <ctype.h>
17 #include <stdarg.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
24 #include "awishcommon.h"
25 #include "vm.h"
28 ////////////////////////////////////////////////////////////////////////////////
29 #define SECRET (42)
32 ////////////////////////////////////////////////////////////////////////////////
33 static const char *ltnames[] = {
34 "GVAR",
35 "TVAR",
36 "SVAR",
37 "CODE",
38 "CONST"
42 ////////////////////////////////////////////////////////////////////////////////
43 #define MAX_TOKEN_LENGTH (256)
45 enum {
46 TK_EOF = -1,
47 TK_ID = 256,
48 TK_NUM,
49 TK_LABELDEF,
51 TK_VM_OPERATOR = 400
55 enum {
56 VMX_DRP = 600,
57 VMX_DUP,
58 VMX_RTN,
59 VMX_RETURN,
60 VMX_SAJ
64 ////////////////////////////////////////////////////////////////////////////////
65 static int optWarnings = 1;
68 ////////////////////////////////////////////////////////////////////////////////
69 static uint8_t vmcode[65536];
70 static int pc = 0;
73 ////////////////////////////////////////////////////////////////////////////////
74 typedef struct MStrListItem {
75 struct MStrListItem *next;
76 char *macname;
77 char *newname;
78 } MStrListItem;
81 typedef struct InputContext {
82 struct InputContext *prev;
83 FILE *fl;
84 char *fname;
85 int lineno;
86 int ucnt;
87 int uca[4];
88 int wasEOF;
89 char *text; // not NULL: macro argument expansion
90 struct MacroDef *macro; // !NULL: we are inside macro expanding
91 int tpos; // position in macro or in text
92 MStrListItem *macnames;
93 MStrListItem *macargs;
94 } InputContext;
97 static InputContext *ictx = NULL;
98 static int token;
99 static char tstr[MAX_TOKEN_LENGTH+1];
100 static int tint;
101 static int incLevel = 0;
104 ////////////////////////////////////////////////////////////////////////////////
105 typedef struct MacroDef {
106 struct MacroDef *next;
107 char *name;
108 char *text;
109 int argc;
110 MStrListItem *argnames;
111 } MacroDef;
114 static MacroDef *macros = NULL;
117 static void freeMStrList (MStrListItem *list) {
118 while (list != NULL) {
119 MStrListItem *c = list;
121 list = c->next;
122 if (c->newname != NULL) free(c->newname);
123 if (c->macname != NULL) free(c->macname);
124 free(c);
129 static void freeMacroList (void) {
130 while (macros != NULL) {
131 MacroDef *mc = macros;
133 macros = mc->next;
134 freeMStrList(mc->argnames);
135 if (mc->name) free(mc->name);
136 if (mc->text) free(mc->text);
137 free(mc);
142 ////////////////////////////////////////////////////////////////////////////////
143 static __attribute__((__noreturn__)) __attribute__((format(printf, 1, 2))) void fatal (const char *fmt, ...) {
144 va_list ap;
146 while (ictx != NULL && ictx->fl == NULL) ictx = ictx->prev;
148 if (ictx != NULL) {
149 fprintf(stderr, "FATAL (line %d, file '%s'): ", ictx->lineno, ictx->fname);
150 } else {
151 fprintf(stderr, "FATAL: ");
153 va_start(ap, fmt);
154 vfprintf(stderr, fmt, ap);
155 va_end(ap);
156 fprintf(stderr, "\n");
157 exit(1);
161 ////////////////////////////////////////////////////////////////////////////////
162 static void addMacroArgDef (MacroDef *mc, const char *name) {
163 MStrListItem *p = NULL, *c;
165 for (MStrListItem *ml = mc->argnames; ml != NULL; p = ml, ml = ml->next) {
166 if (strcmp(ml->macname, name) == 0) fatal("duplicate macro argument name: '%s'", name);
168 if ((c = calloc(1, sizeof(MStrListItem))) == NULL) fatal("out of memory");
169 if ((c->macname = strdup(name)) == NULL) fatal("out of memory");
171 if (p != NULL) {
172 p->next = c;
173 } else {
174 mc->argnames = c;
176 ++(mc->argc);
180 static MStrListItem *genUniqueMacName (MStrListItem *list, const char *macname) {
181 MStrListItem *res;
182 static int count = 0;
184 for (res = list; res != NULL; res = res->next) if (strcmp(res->macname, macname) == 0) return list;
185 if ((res = calloc(1, sizeof(MStrListItem))) == NULL) fatal("out of memory");
186 if ((res->macname = strdup(macname)) == NULL) fatal("out of memory");
187 if ((res->newname = calloc(1, 128)) == NULL) fatal("out of memory");
188 sprintf(res->newname, ". lmd #%d", count++);
189 res->next = list;
190 return res;
194 static MStrListItem *addMacroArg (MStrListItem *args, const char *argname, const char *argtext) {
195 MStrListItem *res;
197 if ((res = calloc(1, sizeof(MStrListItem))) == NULL) fatal("out of memory");
198 if ((res->macname = strdup(argname)) == NULL) fatal("out of memory");
199 if ((res->newname = strdup(argtext)) == NULL) fatal("out of memory");
200 res->next = args;
201 return res;
205 ////////////////////////////////////////////////////////////////////////////////
206 static void openFile (const char *fname) {
207 InputContext *ic = calloc(1, sizeof(InputContext));
209 if (ic == NULL) fatal("out of memory");
210 ic->fl = fopen(fname, "r");
211 if (ic->fl == NULL) fatal("can't open file: '%s'", fname);
212 ic->prev = ictx;
213 ic->fname = strdup(fname);
214 #ifdef _WIN32
215 for (char *t = ic->fname; *t; ++t) if (*t == '\\') *t = '/';
216 #endif
217 ic->lineno = 1;
218 ictx = ic;
219 for (int f = 0; f < incLevel; ++f) fputc(' ', stderr);
220 ++incLevel;
221 fprintf(stderr, "compiling: %s\n", ic->fname);
225 static void openText (const char *text) {
226 InputContext *ic = calloc(1, sizeof(InputContext));
228 if (ic == NULL) fatal("out of memory");
229 ic->prev = ictx;
230 ic->lineno = 1;
231 if ((ic->text = strdup(text)) == NULL) fatal("out of memory");
232 ictx = ic;
236 static void openMacro (MacroDef *mac, MStrListItem *args) {
237 InputContext *ic = calloc(1, sizeof(InputContext));
239 if (ic == NULL) fatal("out of memory");
240 ic->prev = ictx;
241 ic->lineno = 1;
242 ic->macro = mac;
243 ic->macargs = args;
244 ictx = ic;
248 static void closeFile (void) {
249 if (ictx != NULL) {
250 InputContext *ic = ictx;
252 ictx = ic->prev;
253 if (ic->fl != NULL) {
254 fclose(ic->fl);
255 --incLevel;
257 if (ic->fname) free(ic->fname);
258 if (ic->text != NULL) free(ic->text);
259 freeMStrList(ic->macnames);
260 freeMStrList(ic->macargs);
261 free(ic);
266 static inline int inText (void) {
267 return (ictx != NULL && ictx->text != NULL);
271 ////////////////////////////////////////////////////////////////////////////////
272 static int nextToken (void);
274 static void transformToken (void) {
275 // check if this is macroarg
276 if (!inText() && (token == TK_LABELDEF || token == TK_ID)) {
277 for (InputContext *ic = ictx; ic != NULL; ic = ic->prev) {
278 for (MStrListItem *ml = ic->macargs; ml != NULL; ml = ml->next) {
279 if (strcmp(tstr, ml->macname) == 0) {
280 if (token != TK_ID) fatal("macro argument redefinitions are prohibited");
281 //printf("marg [%s]: [%s]\n", ml->macname, ml->newname);
282 openText(ml->newname);
283 nextToken();
284 return;
289 // check macro labels
290 if (!inText() && tstr[0] == '$' && (token == TK_LABELDEF || token == TK_ID)) {
291 for (InputContext *ic = ictx; ic != NULL; ic = ic->prev) {
292 for (MStrListItem *ml = ic->macnames; ml != NULL; ml = ml->next) {
293 if (strcmp(tstr, ml->macname) == 0) { strcpy(tstr, ml->newname); return; }
297 // not found or in text; generate new macro name
298 if (!inText() && tstr[0] == '$' && (token == TK_LABELDEF || token == TK_ID)) {
299 for (InputContext *ic = ictx; ic != NULL; ic = ic->prev) {
300 if (ic->macro != NULL) {
301 strcpy(tstr, (ic->macnames = genUniqueMacName(ic->macnames, tstr))->newname);
302 break;
309 static void ungetChar (int c) {
310 if (ictx != NULL) {
311 if (ictx->ucnt >= 4) fatal("too many unread chars");
312 ictx->uca[ictx->ucnt++] = c;
317 static int tokenWantFileName = 0;
319 static int nextChar (void) {
320 int c;
322 if (ictx == NULL) return EOF;
323 if (ictx->ucnt > 0) {
324 c = ictx->uca[--ictx->ucnt];
325 } else {
326 if (ictx->wasEOF) {
327 c = EOF;
328 } else if (ictx->fl != NULL) {
329 c = fgetc(ictx->fl);
330 if (c == 0) c = ' ';
331 } else if (ictx->text != NULL) {
332 c = ictx->text[ictx->tpos++];
333 if (c == 0) { --(ictx->tpos); c = EOF; }
334 } else {
335 c = ictx->macro->text[ictx->tpos++];
336 if (c == 0) { --(ictx->tpos); c = EOF; }
338 if (c == EOF) {
339 if (ictx->wasEOF) {
340 closeFile();
341 c = (ictx==NULL ? EOF : '\n');
342 } else {
343 ictx->wasEOF = 1;
344 c = ' ';
346 } else {
347 if (c == '\n') ++(ictx->lineno);
350 if (!tokenWantFileName) {
351 if (c >= 'A' && c <= 'Z') c += 32; // tolower
353 return c;
357 static void skipSpaces (int allowNL) {
358 for (;;) {
359 int c = nextChar();
361 if (c == EOF) return;
362 if (c == '\n' && !allowNL) { ungetChar(c); return; }
363 if (c == ';') {
364 do { c = nextChar(); } while (c != EOF && c != '\n');
365 if (!allowNL) { if (c != EOF) ungetChar(c); return; }
366 continue;
368 if (c == '/') {
369 int c1 = nextChar();
371 if (c1 == '/') {
372 do { c = nextChar(); } while (c != EOF && c != '\n');
373 if (!allowNL) { if (c != EOF) ungetChar(c); return; }
374 continue;
376 if (c1 == '*') {
377 int wasNL = 0;
379 for (;;) {
380 c = nextChar();
381 if (c == EOF) break;
382 if (c == '\n') wasNL = 1;
383 if (c == '*') {
384 c = nextChar();
385 if (c == '\n') wasNL = 1;
386 if (c == '/') break;
387 if (c == EOF) fatal("unterminated comment");
390 if (!allowNL && wasNL) { ungetChar('\n'); return; }
391 continue;
393 ungetChar(c1);
394 ungetChar(c);
395 break;
397 if (c > 32) { ungetChar(c); break; }
402 static int digit (int c, int base) {
403 if (c == EOF) return -1;
404 if (c >= 'a' && c <= 'z') c -= 32;
405 if (c > '9' && c < 'A') return -1;
406 if (c > '9') c -= 7;
407 c -= '0';
408 if (c < 0 || c >= base) return -1;
409 return c;
413 static void getNumber (int c) {
414 int base = 10;
416 token = TK_NUM;
417 tint = 0;
418 if (c == '0') {
419 c = nextChar();
420 if (c == EOF) return;
421 if (!isdigit(c)) {
422 switch (c) {
423 case 'b': case 'B': base = 2; break;
424 case 'o': case 'O': base = 8; break;
425 case 'd': case 'D': base = 10; break;
426 case 'x': case 'X': base = 16; break;
427 default:
428 if (isalpha(c)) fatal("invalid number");
429 if (c != EOF) ungetChar(c);
430 return;
432 c = nextChar();
433 if (digit(c, base) < 0) fatal("invalid number");
437 for (;;) {
438 int d = digit(c, base);
440 if (d < 0) break;
441 tint = (tint*base)+d;
442 if (tint > 32767) fatal("number constant too big");
443 c = nextChar();
445 if (c != EOF && isalpha(c)) fatal("invalid number");
446 if (c != EOF) ungetChar(c);
450 static int nextToken (void) {
451 int c;
453 memset(tstr, 0, sizeof(tstr));
454 tint = 0;
455 token = TK_EOF;
456 skipSpaces(1);
457 if (ictx == NULL) return TK_EOF;
458 c = nextChar();
459 if (c == EOF) return TK_EOF;
460 if (c >= 127) fatal("invalid char (%d)", c);
461 tstr[0] = c;
462 tstr[1] = 0;
464 if (isalpha(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
465 // id or label
466 int f = 1;
468 for (;;) {
469 c = nextChar();
470 if (isalnum(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
471 if (f >= MAX_TOKEN_LENGTH-1) fatal("identifier too long");
472 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
473 tstr[f++] = c;
474 } else {
475 //if (c != ';' && c > 32) fatal("invalid identifier");
476 break;
479 tstr[f] = 0;
480 if (!isalnum(tstr[0]) && !tstr[1]) {
481 if (c != EOF) ungetChar(c);
482 token = tstr[0];
483 return token;
485 token = TK_ID;
486 // label definition?
487 if (!tokenWantFileName) {
488 if (c == ':') {
489 token = TK_LABELDEF;
490 } else {
491 if (c != EOF) ungetChar(c);
493 // vm mnemonics?
494 if (f == 3) {
495 for (f = 0; vmOpNames[f]; ++f) {
496 if (strcmp(tstr, vmOpNames[f]) == 0) {
497 if (token == TK_LABELDEF) fatal("invalid label: '%s'", tstr);
498 token = TK_VM_OPERATOR+f;
499 break;
502 if (token < TK_VM_OPERATOR) {
503 if (strcmp(tstr, "drp") == 0) token = VMX_DRP;
504 else if (strcmp(tstr, "dup") == 0) token = VMX_DUP;
505 else if (strcmp(tstr, "rtn") == 0) token = VMX_RTN;
506 else if (strcmp(tstr, "saj") == 0) token = VMX_SAJ;
508 } else if (strcmp(tstr, "return") == 0) {
509 token = VMX_RETURN;
511 if (strcmp(tstr, "@@") == 0) strcpy(tstr, "."); // special label
512 transformToken(); // macro transformations
514 } else if (c == '-' || c == '+') {
515 int neg = (c=='-');
517 token = c;
518 if ((c = nextChar()) != EOF) {
519 if (isdigit(c)) {
520 getNumber(c);
521 if (neg) tint = -tint;
522 } else {
523 ungetChar(c);
526 } else if (isdigit(c)) {
527 // number
528 getNumber(c);
529 } else {
530 // delimiter
531 token = c;
533 return token;
537 ////////////////////////////////////////////////////////////////////////////////
538 typedef struct LabelRefInfo {
539 struct LabelRefInfo *next;
540 int pc;
541 int size;
542 } LabelRefInfo;
545 typedef struct LabelInfo {
546 struct LabelInfo *next;
547 char *name;
548 int type; // LB_XXXX
549 int value; // code && <0: not defined yet (forward ref)
550 int ext; // extern (<0), public(>0), normal(0)
551 LabelRefInfo *fixes; // for undefined code labels
552 LabelRefInfo *refs; // for externals
553 int used;
554 } LabelInfo;
557 static LabelInfo *labels = NULL;
558 static LabelRefInfo *relrefs = NULL; // info for relocaions
559 static LabelRefInfo *refFwd = NULL; // @@f references (will be resolved on next @@)
560 static int prevTmpLabelPC = -1; // previous @@ PC (-1: not defined yet)
561 static LabelInfo labelTempBack, labelTempFwd;
563 static int vglast = 0;
564 static int vtlast = 0;
565 static int vtloc = VM_VARS_SIZE-1; // local thread (var 126 is used for internal thing)
568 typedef struct VarFixup {
569 struct VarFixup *next;
570 int pc;
571 } VarFixup;
574 static VarFixup *gvfixes = NULL;
575 static VarFixup *tvfixes = NULL;
577 static VarFixup *addVarFixup (VarFixup *list, int pc) {
578 VarFixup *res = calloc(1, sizeof(VarFixup));
580 if (res == NULL) fatal("out of memory");
581 res->next = list;
582 res->pc = pc;
583 return res;
587 static void addGVarFixup (int pc) {
588 gvfixes = addVarFixup(gvfixes, pc);
592 static void addTVarFixup (int pc) {
593 tvfixes = addVarFixup(tvfixes, pc);
597 static LabelRefInfo *lrefRemoveDups (LabelRefInfo *list) {
598 LabelRefInfo *p = NULL, *c = list;
600 list = NULL;
601 while (c != NULL) {
602 int dup = 0;
604 if (list != NULL) {
605 for (LabelRefInfo *r = list; r != c; r = r->next) if (r->pc == c->pc && r->size == c->size) { dup = 1; break; }
608 if (dup) {
609 // remove
610 LabelRefInfo *n = c->next;
612 if (p != NULL) p->next = n;
613 free(c);
614 c = n;
615 } else {
616 // keep
617 if (p == NULL) list = c;
618 p = c;
619 c = c->next;
622 return list;
626 // returns new head (created item)
627 static LabelRefInfo *addLabelRefToList (LabelRefInfo *list, int pc) {
628 LabelRefInfo *res;
630 res = calloc(1, sizeof(LabelRefInfo));
631 if (res == NULL) fatal("out of memory");
632 res->next = list;
633 res->pc = pc;
634 res->size = 2;
635 return res;
639 static void freeLabelRefList (LabelRefInfo *list) {
640 while (list != NULL) {
641 LabelRefInfo *r = list;
643 list = r->next;
644 free(r);
649 static void addExternRef (LabelInfo *l, int pc, int size) {
650 if (l == &labelTempBack || l == &labelTempFwd) return;
651 if (l != NULL && l->ext < 0) {
652 if (size <= 0) {
653 size = (l->type == LB_CODE || l->type == LB_CONST || l->type == LB_SVAR) ? 2 : 1;
655 l->refs = addLabelRefToList(l->refs, pc);
656 l->refs->size = size;
661 static void addLabelRef (LabelInfo *l, int pc) {
662 if (l == &labelTempBack) {
663 if (prevTmpLabelPC < 0) fatal("no backward '@@' defined");
664 //fprintf(stderr, "backref to '@@'\n");
665 vmcode[pc+0] = prevTmpLabelPC&0xff;
666 vmcode[pc+1] = (prevTmpLabelPC>>8)&0xff;
667 relrefs = addLabelRefToList(relrefs, pc);
668 return;
670 if (l == &labelTempFwd) {
671 //fprintf(stderr, "fwdref to '@@'\n");
672 refFwd = addLabelRefToList(refFwd, pc);
673 relrefs = addLabelRefToList(relrefs, pc);
674 return;
676 if (l != NULL) {
677 l->used = 1;
678 addExternRef(l, pc, -1);
679 if (l->type == LB_CODE && l->ext >= 0) {
680 // record fixup info for undefined code labels
681 if (l->value < 0) l->fixes = addLabelRefToList(l->fixes, pc);
682 // record reference info for code labels
683 relrefs = addLabelRefToList(relrefs, pc);
689 static void fixupFwdTmpRefs (void) {
690 for (LabelRefInfo *fix = refFwd; fix != NULL; fix = fix->next) {
691 vmcode[fix->pc+0] = pc&0xff;
692 vmcode[fix->pc+1] = (pc>>8)&0xff;
694 freeLabelRefList(refFwd);
695 refFwd = NULL;
699 static void fixupLabelRefs (LabelInfo *l, int pc) {
700 if (l == &labelTempBack || l == &labelTempFwd) return;
701 if (l != NULL) {
702 l->used = 1;
703 l->value = pc;
704 for (LabelRefInfo *fix = l->fixes; fix != NULL; fix = fix->next) {
705 vmcode[fix->pc+0] = (l->value)&0xff;
706 vmcode[fix->pc+1] = ((l->value)>>8)&0xff;
708 freeLabelRefList(l->fixes);
709 l->fixes = NULL;
714 static void checkLabels (void) {
715 if (refFwd != NULL) fatal("unresolved forward references to '@@' found");
716 for (LabelInfo *l = labels; l != NULL; l = l->next) {
717 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
718 if (!l->used) {
719 l->used = -1;
720 if (optWarnings && l->type != LB_CONST && strcmp(l->name, "retval") != 0 && l->ext == 0) fprintf(stderr, "WARNING: unused %s label '%s'\n", ltnames[l->type], l->name);
726 static void freeLabels (void) {
727 checkLabels();
728 while (labels) {
729 LabelInfo *l = labels;
731 labels = l->next;
732 freeLabelRefList(l->fixes);
733 freeLabelRefList(l->refs);
734 free(l->name);
735 free(l);
740 static void freeLocalLabels (int onlyVars) {
741 LabelInfo *p = NULL, *l = labels;
743 if (refFwd != NULL) fatal("unresolved references to '@@' found");
744 prevTmpLabelPC = -1;
745 while (l != NULL) {
746 LabelInfo *n = l->next;
748 if ((!onlyVars || l->type != LB_CODE) && l->name[0] == '.') {
749 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
750 if (!l->used) {
751 l->used = -1;
752 if (optWarnings && l->type != LB_CONST && strcmp(l->name, "retval") != 0 && l->ext == 0) fprintf(stderr, "WARNING: unused %s label '%s'\n", ltnames[l->type], l->name);
754 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
755 freeLabelRefList(l->fixes);
756 freeLabelRefList(l->refs);
757 free(l->name);
758 free(l);
759 if (p != NULL) p->next = n; else labels = n;
760 } else {
761 p = l;
763 l = n;
768 static AWISH_PURE LabelInfo *findLabel (const char *name) {
769 if (name && strcmp(name, "@@b") == 0) return &labelTempBack;
770 if (name && strcmp(name, "@@f") == 0) return &labelTempFwd;
771 if (name && strcmp(name, ".") == 0) {
772 //fprintf(stderr, "LOOKUP: '@@'\n");
773 return NULL;
775 if (name && name[0] == '@') ++name;
776 if (!name || !name[0]) return NULL;
777 for (LabelInfo *l = labels; l != NULL; l = l->next) if (strcmp(l->name, name) == 0) return l;
778 return NULL;
782 static LabelInfo *addLabel (const char *name) {
783 LabelInfo *l;
785 if (!name || !name[0]) fatal("internal error: empty label name");
786 if (strcmp(name, "@@b") == 0 || strcmp(name, "@@f") == 0 || strcmp(name, ".") == 0) fatal("can't define special label: '%s'", name);
787 if (findLabel(name)) fatal("duplicate label: '%s'", name);
788 l = calloc(1, sizeof(LabelInfo));
789 if (l == NULL) fatal("out of memory");
790 l->name = strdup(name);
791 if (l->name == NULL) fatal("out of memory");
792 l->type = LB_CODE;
793 l->value = -1;
794 l->ext = 0;
795 l->fixes = NULL;
796 l->refs = NULL;
797 l->next = labels;
798 labels = l;
799 return l;
803 static void newTempLabel (void) {
804 fixupFwdTmpRefs();
805 prevTmpLabelPC = pc;
806 //fprintf(stderr, "new '@@'\n");
810 ////////////////////////////////////////////////////////////////////////////////
811 typedef struct OperandInfo {
812 LabelInfo *l; // !=NULL: label
813 int var; // [...]
814 int value; // if l==NULL
815 int vartype; // 0: global; 1: tlocal; 2: stack
816 struct OperandInfo *next;
817 } OperandInfo;
820 static int stackAdj = 0;
823 static void setIntOperand (OperandInfo *op, int value) {
824 op->l = NULL;
825 op->var = 0;
826 op->value = value;
827 op->vartype = -1;
831 static void setLabelOperand (OperandInfo *op, LabelInfo *l) {
832 op->l = l;
833 op->var = 0;
834 op->value = 0;
835 op->vartype = -1;
839 static void getOperand (OperandInfo *op, int wantCode) {
840 if (token == TK_ID) {
841 LabelInfo *l = findLabel(tstr);
843 if (l == NULL) {
844 // new code label
845 l = addLabel(tstr);
846 l->type = LB_CODE;
847 l->value = -1;
848 } else {
849 if (wantCode && l->type != LB_CODE) fatal("code offset expected");
851 setLabelOperand(op, l);
852 nextToken();
853 return;
856 if (token == TK_NUM) {
857 if (wantCode) fatal("code offset expected");
858 setIntOperand(op, tint);
859 nextToken();
860 return;
863 if (token == '[') {
864 //if (wantCode) fatal("code offset expected");
865 nextToken();
866 if (token == TK_ID) {
867 LabelInfo *l = findLabel(tstr);
869 if (l == NULL || l->type == LB_CODE) fatal("unknown variable: '%s'", tstr);
870 setLabelOperand(op, l);
871 } else if (token == '@' || token == '.') {
872 int loc = token;
874 if (nextToken() != TK_NUM) fatal("index expected");
875 setIntOperand(op, tint);
876 if (loc != '.') {
877 // not stack
878 if (tint < 0 || tint > 126) fatal("invalid variable index");
879 op->vartype = loc=='@' ? 0 : 1;
880 } else {
881 // stack
882 op->vartype = 2;
884 } else if (token == TK_NUM) {
885 // local
886 if (tint < 0 || tint > 126) fatal("invalid variable index");
887 setIntOperand(op, tint);
888 op->vartype = 1;
889 } else {
890 fatal("invalid operand");
892 op->var = 1;
893 if (nextToken() != ']') fatal("']' expected");
894 nextToken();
895 return;
898 fprintf(stderr, "*%d [%s] %d\n", token, tstr, tint);
899 fatal("invalid operand");
903 static inline int hasOperand (void) {
904 return (token != TK_EOF && token != TK_LABELDEF && token < TK_VM_OPERATOR);
908 ////////////////////////////////////////////////////////////////////////////////
909 static void emitByte (int b) {
910 if (b < 0 || b > 255) fatal("internal error");
911 if (pc >= 32768) fatal("code too big");
912 vmcode[pc++] = b;
916 static void emitOpCode (int opc, int opcount) {
917 if (opc < 0 || opc > 63 || opcount < 0 || opcount > 3) fatal("internal error");
918 emitByte(opc|(opcount<<6));
923 static void emitInteger (int val) {
924 emitByte(255); // special
925 emitByte(val&0xff);
926 emitByte((val>>8)&0xff);
931 static void emitOperand (OperandInfo *op) {
932 if (!op || op->var < 0) return;
934 if (op->var > 0) {
935 // variable
936 if (op->l) {
937 // from label
938 op->l->used = 1;
939 switch (op->l->type) {
940 case LB_GVAR:
941 emitByte(op->l->value|0x80);
942 addLabelRef(op->l, pc-1);
943 if (op->l->ext >= 0 && op->l->name[0] != '.') addGVarFixup(pc-1);
944 break;
945 case LB_TVAR:
946 emitByte(op->l->value);
947 addLabelRef(op->l, pc-1);
948 if (op->l->ext >= 0 && op->l->name[0] != '.') addTVarFixup(pc-1);
949 break;
950 case LB_SVAR:
951 emitByte(127); // special
952 if (op->l->value < 0) {
953 emitByte((op->l->value-stackAdj)&0xff);
954 emitByte(((op->l->value-stackAdj)>>8)&0xff);
955 } else {
956 emitByte(op->l->value&0xff);
957 emitByte((op->l->value>>8)&0xff);
959 addLabelRef(op->l, pc-2);
960 break;
961 default: fatal("internal error");
963 } else {
964 // from value
965 switch (op->vartype) {
966 case 0: emitByte(op->value|0x80); break; // global
967 case 1: emitByte(op->value); break; // tlocal
968 case 2: // stack
969 emitByte(127); // special
970 if (op->value < 0) {
971 emitByte((op->value-stackAdj)&0xff);
972 emitByte(((op->value-stackAdj)>>8)&0xff);
973 } else {
974 emitByte(op->value&0xff);
975 emitByte((op->value>>8)&0xff);
977 break;
978 default: fatal("internal error");
981 return;
983 // code label
984 if (op->var == 0) {
985 // immediate
986 emitByte(255); // special
987 if (op->l) {
988 // from label
989 op->l->used = 1;
990 emitByte(op->l->value&0xff);
991 emitByte((op->l->value>>8)&0xff);
992 addLabelRef(op->l, pc-2);
993 } else {
994 // direct
995 if (op->value < -32767 || op->value > 32767) fatal("invalid value");
996 emitByte(op->value&0xff);
997 emitByte((op->value>>8)&0xff);
1003 static void emitInstruction (int opc, OperandInfo *op0, OperandInfo *op1, OperandInfo *op2) {
1004 int ocnt = 0;
1006 if (op0 && op0->var >= 0) ++ocnt;
1007 if (ocnt == 1 && op1 && op1->var >= 0) ++ocnt;
1008 if (ocnt == 2 && op2 && op2->var >= 0) ++ocnt;
1009 emitOpCode(opc, ocnt);
1010 if (ocnt > 0) emitOperand(op0);
1011 if (ocnt > 1) emitOperand(op1);
1012 if (ocnt > 2) emitOperand(op2);
1016 ////////////////////////////////////////////////////////////////////////////////
1017 static void doNoOperands (int opcode) {
1018 emitInstruction(opcode, NULL, NULL, NULL);
1022 static void doMath (int opcode) {
1023 OperandInfo op0, op1, op2;
1025 op0.var = op1.var = op2.var = -1;
1026 if (hasOperand()) {
1027 getOperand(&op0, 0);
1028 if (token == ',') {
1029 nextToken();
1030 getOperand(&op1, 0);
1031 if (token == ',') {
1032 nextToken();
1033 getOperand(&op2, 0);
1034 if (op2.var != 1) fatal("variable expected as third operand");
1035 } else {
1036 if (op0.var != 1) fatal("variable expected as first operand");
1040 emitInstruction(opcode, &op0, &op1, &op2);
1044 static void doJXX (int opcode) {
1045 OperandInfo op0, op1, op2;
1047 op0.var = op1.var = op2.var = -1;
1048 if (hasOperand()) {
1049 getOperand(&op0, 1);
1050 if (token == ',') {
1051 nextToken();
1052 getOperand(&op1, 0);
1053 if (token == ',') {
1054 nextToken();
1055 getOperand(&op2, 0);
1059 emitInstruction(opcode, &op0, &op1, &op2);
1063 static void doBranch (int opcode) {
1064 OperandInfo op0;
1066 op0.var = -1;
1067 if (hasOperand()) {
1068 getOperand(&op0, 1);
1070 emitInstruction(opcode, &op0, NULL, NULL);
1074 static void doBSR (int opcode) {
1075 OperandInfo op0, *op1 = NULL, *op2 = NULL, *olist = NULL, *c = NULL;
1076 int osadj = stackAdj, ocnt = 0;;
1078 op0.var = -1;
1079 if (hasOperand()) {
1080 getOperand(&op0, (opcode == VM_BSR) ? 1 : 0);
1081 // collect other operands
1082 while (token == ',') {
1083 OperandInfo *o = calloc(sizeof(OperandInfo), 1);
1085 if (o == NULL) fatal("out of memory");
1086 nextToken();
1087 getOperand(o, 0);
1088 if (olist != NULL) c->next = o; else olist = o;
1089 c = o;
1090 o->next = NULL;
1091 ++ocnt;
1094 switch (ocnt) {
1095 case 1:
1096 op1 = olist;
1097 olist = NULL;
1098 break;
1099 case 2:
1100 op1 = olist;
1101 op2 = olist->next;
1102 olist = NULL;
1103 break;
1105 // generate pushes
1106 while (olist != NULL) {
1107 OperandInfo *o = olist;
1109 if (o->next->next == NULL) {
1110 // last 2 operands
1111 op1 = olist;
1112 op2 = olist->next;
1113 olist = NULL;
1114 break;
1116 olist = o->next;
1117 emitInstruction(VM_PSH, o, NULL, NULL);
1118 ++stackAdj;
1119 free(o);
1122 emitInstruction(opcode, &op0, op1, op2);
1123 if (op2 != NULL) free(op2);
1124 if (op1 != NULL) free(op1);
1125 stackAdj = osadj;
1129 static void doMap (int opcode, int lastMBV) {
1130 OperandInfo op0, op1, op2;
1132 op0.var = op1.var = op2.var = -1;
1133 if (hasOperand()) {
1134 getOperand(&op0, 0);
1135 if (token == ',') {
1136 nextToken();
1137 getOperand(&op1, 0);
1138 if (token == ',') {
1139 nextToken();
1140 getOperand(&op2, 0);
1144 emitInstruction(opcode, &op0, &op1, &op2);
1148 static void doRST (int opcode) {
1149 doBSR(opcode);
1153 static void doSet (int opcode) {
1154 OperandInfo op0, op1, op2;
1156 op0.var = op1.var = op2.var = -1;
1157 getOperand(&op0, 0);
1158 if (token == ',') {
1159 nextToken();
1160 getOperand(&op1, 0);
1161 if (token == ',') {
1162 nextToken();
1163 getOperand(&op2, 0);
1166 if (op2.var < 0 && op0.var != 1) fatal("first operand should be var");
1167 emitInstruction(opcode, &op0, &op1, &op2);
1171 static void doDrop (int opcode) {
1172 OperandInfo op0;
1174 op0.var = -1;
1175 if (hasOperand()) getOperand(&op0, 0);
1176 if (op0.var > 0) fatal("number expected");
1177 if (op0.value < 0) fatal("positive number expected");
1178 emitInstruction(opcode, &op0, NULL, NULL);
1182 static void doPush (int opcode) {
1183 OperandInfo op0, op1, op2;
1185 op0.var = op1.var = op2.var = -1;
1186 getOperand(&op0, 0);
1187 if (token == ',') {
1188 nextToken();
1189 getOperand(&op1, 0);
1190 if (token == ',') {
1191 nextToken();
1192 getOperand(&op2, 0);
1195 emitInstruction(opcode, &op0, &op1, &op2);
1199 static void doPop (int opcode) {
1200 OperandInfo op0, op1, op2;
1202 op0.var = op1.var = op2.var = -1;
1203 if (hasOperand()) {
1204 getOperand(&op0, 0);
1205 if (token == ',') {
1206 nextToken();
1207 getOperand(&op1, 0);
1208 if (token == ',') {
1209 nextToken();
1210 getOperand(&op2, 0);
1214 emitInstruction(opcode, &op0, &op1, &op2);
1218 static void doSwap (int opcode) {
1219 OperandInfo op0, op1;
1221 op0.var = op1.var = -1;
1222 if (hasOperand()) {
1223 getOperand(&op0, 0);
1224 if (token == ',') {
1225 nextToken();
1226 getOperand(&op1, 0);
1229 emitInstruction(opcode, &op0, &op1, NULL);
1233 static void doDepth (int opcode) {
1234 OperandInfo op0;
1236 op0.var = -1;
1237 if (hasOperand()) getOperand(&op0, 0);
1238 emitInstruction(opcode, &op0, NULL, NULL);
1242 static void doPickRoll (int opcode) {
1243 OperandInfo op0, op1;
1245 op0.var = op1.var = -1;
1246 if (hasOperand()) {
1247 getOperand(&op0, 0);
1248 if (token == ',') {
1249 nextToken();
1250 getOperand(&op1, 0);
1251 if (op1.var != 1) fatal("second argument must be variable");
1254 emitInstruction(opcode, &op0, &op1, NULL);
1258 static void doNew (int opcode) {
1259 OperandInfo op0, op1;
1261 op0.var = op1.var = -1;
1262 if (hasOperand()) {
1263 getOperand(&op0, 0);
1264 if (token == ',') {
1265 nextToken();
1266 getOperand(&op1, 0);
1269 emitInstruction(opcode, &op0, &op1, NULL);
1273 static void doTId (int opcode) {
1274 OperandInfo op0;
1276 op0.var = -1;
1277 if (hasOperand()) getOperand(&op0, 0);
1278 emitInstruction(opcode, &op0, NULL, NULL);
1282 static void doKillSusRes (int opcode) {
1283 OperandInfo op0;
1285 op0.var = -1;
1286 if (hasOperand()) getOperand(&op0, 0);
1287 emitInstruction(opcode, &op0, NULL, NULL);
1291 static void doSta (int opcode) {
1292 OperandInfo op0, op1;
1294 op0.var = op1.var = -1;
1295 if (hasOperand()) {
1296 getOperand(&op0, 0);
1297 if (token == ',') {
1298 nextToken();
1299 getOperand(&op1, 0);
1300 if (op1.var != 1) fatal("variable expected");
1303 emitInstruction(opcode, &op0, &op1, NULL);
1307 static void doGet (int opcode) {
1308 OperandInfo op0, op1, op2;
1310 op0.var = op1.var = op2.var = -1;
1311 getOperand(&op0, 0);
1312 if (token != ',') fatal("at least two operands expected");
1313 nextToken();
1314 getOperand(&op1, 0);
1315 if (token == ',') {
1316 nextToken();
1317 getOperand(&op2, 0);
1318 if (op2.var != 1) fatal("variable expected as third operand");
1320 emitInstruction(opcode, &op0, &op1, &op2);
1324 static void doRXC (int opcode) {
1325 OperandInfo op0, op1;
1327 op0.var = op1.var = -1;
1328 if (hasOperand()) {
1329 getOperand(&op0, 0);
1330 if (token == ',') {
1331 nextToken();
1332 getOperand(&op1, 0);
1333 if (op1.var != 1) fatal("variable expected as second operand");
1336 emitInstruction(opcode, &op0, &op1, NULL);
1340 static void doWXC (int opcode) {
1341 OperandInfo op0, op1;
1343 op0.var = op1.var = -1;
1344 if (hasOperand()) {
1345 getOperand(&op0, 0);
1346 if (token == ',') {
1347 nextToken();
1348 getOperand(&op1, 0);
1351 emitInstruction(opcode, &op0, &op1, NULL);
1355 static void doRet (int opcode) {
1356 OperandInfo op0, op1, op2;
1358 op0.var = op1.var = op2.var = -1;
1359 if (hasOperand()) {
1360 getOperand(&op0, 0);
1361 if (token != ',') fatal("at least two operands expected");
1362 nextToken();
1363 getOperand(&op1, 0);
1364 if (token == ',') {
1365 nextToken();
1366 getOperand(&op2, 0);
1369 emitInstruction(opcode, &op0, &op1, &op2);
1373 static void doSAj (int opcode) {
1374 if (token != TK_NUM) fatal("number expected");
1375 stackAdj += tint;
1376 nextToken();
1380 ////////////////////////////////////////////////////////////////////////////////
1381 static void parseInlcude (void) {
1382 static char fname[8192], *t;
1384 tokenWantFileName = 1;
1385 if (nextToken() != TK_ID) fatal("identifier expected");
1386 tokenWantFileName = 0;
1387 strcpy(fname, ictx->fname);
1388 #ifdef _WIN32
1389 for (t = fname; *t; ++t) if (*t == '\\') *t = '/';
1390 #endif
1391 t = strrchr(fname, '/');
1392 if (t != NULL) t[1] = 0; else fname[0] = 0;
1393 strcat(fname, tstr);
1394 if (incLevel > 64) fatal("too many includes");
1395 openFile(fname);
1396 nextToken();
1400 static void parseVarList (int type, int local, int gotext) {
1401 for (;;) {
1402 LabelInfo *l;
1404 if (nextToken() != TK_ID) fatal("identifier expected");
1405 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1406 if (tstr[0] == '.' && local <= 0) fatal("invalid variable name: '%s'", tstr);
1407 if (tstr[0] != '.' && local > 0) fatal("invalid variable name: '%s'", tstr);
1408 l = findLabel(tstr);
1409 if (l != NULL) {
1410 if (gotext) {
1411 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1412 if (l->type != type) fatal("can't change existing extern label type: '%s'", tstr);
1413 } else {
1414 fatal("duplicate variable or label: '%s'", tstr);
1417 if (l == NULL) {
1418 l = addLabel(tstr);
1419 l->type = type;
1420 if (local > 0 && vtloc <= vtlast) fatal("too many local vars");
1421 if (gotext) {
1422 l->value = 0;
1423 } else {
1424 l->value = (local>0)?(--vtloc):(type==LB_GVAR?vglast++:vtlast++);
1426 if (local < 0) l->ext = 1;
1428 if (gotext) l->ext = -1;
1429 if (l->value > 126) fatal("too many vars");
1430 if (nextToken() != ',') break;
1435 static void parseUsedVarList (void) {
1436 for (;;) {
1437 LabelInfo *l;
1439 if (nextToken() != TK_ID) fatal("identifier expected");
1440 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1441 l = findLabel(tstr);
1442 if (l == NULL) fatal("unknown label: '%s'", tstr);
1443 l->used = 1;
1444 if (nextToken() != ',') break;
1449 static void parseLocList (void) {
1450 for (;;) {
1451 LabelInfo *l;
1453 if (nextToken() != TK_ID) fatal("identifier expected");
1454 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1455 if (tstr[0] != '.') fatal("local identifier expected instead of '%s'", tstr);
1456 l = findLabel(tstr);
1457 if (l != NULL) fatal("can't redefine label as local: '%s'", tstr);
1458 l = addLabel(tstr);
1459 l->ext = 0;
1460 l->type = LB_SVAR;
1461 l->value = -1;
1462 if (nextToken() != '=') fatal("'=' expected");
1463 if (nextToken() != TK_NUM) fatal("number expected");
1464 l->value = tint;
1465 if (nextToken() != ',') break;
1470 static void parseConst (int ext, int gotext) {
1471 LabelInfo *l;
1473 if (nextToken() != TK_ID) fatal("identifier expected");
1474 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1475 if (tstr[0] == '.') fatal("invalid constant name: '%s'", tstr);
1476 l = findLabel(tstr);
1477 if (l != NULL) {
1478 if (gotext) {
1479 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1480 if (l->type != LB_CONST) fatal("can't change existing extern label type: '%s'", tstr);
1481 } else {
1482 fatal("constant must be unique: '%s'", tstr);
1485 if (l == NULL) {
1486 l = addLabel(tstr);
1487 l->type = LB_CONST;
1488 l->value = 0;
1490 if (!gotext) {
1491 if (nextToken() == '=') nextToken();
1492 if (token != TK_NUM) fatal("constant must be numeric");
1493 l->value = tint;
1495 if (ext) l->ext = 1;
1496 if (gotext) l->ext = -1;
1497 nextToken();
1501 static char procname[8192];
1502 static int proclocals = 0;
1503 static int procargs = 0;
1504 static int lastWasReturn = 0;
1507 static void parseProc (int ext) {
1508 LabelInfo *l, *a = NULL;
1509 int spt;
1511 lastWasReturn = 0;
1512 if (nextToken() != TK_ID) fatal("identifier expected");
1513 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1514 if (tstr[0] == '.') fatal("invalid proc name: '%s'", tstr);
1515 if (procname[0]) fatal("unclosed proc: '%s'", procname);
1516 strcpy(procname, tstr);
1517 proclocals = 0;
1518 procargs = 0;
1519 freeLocalLabels(0); // vars and code
1521 l = findLabel(tstr);
1522 if (l != NULL) {
1523 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate proc label: '%s'", tstr);
1524 fixupLabelRefs(l, pc);
1525 } else {
1526 l = addLabel(tstr);
1527 l->type = LB_CODE;
1528 l->value = pc;
1530 if (ext) l->ext = 1;
1532 nextToken();
1534 while (token == TK_LABELDEF && (strcmp(tstr, "arg") == 0 || strcmp(tstr, "args") == 0)) {
1535 for (;;) {
1536 if (nextToken() != TK_ID) fatal("identifier expected");
1537 if (tstr[0] != '.') fatal("argument name must starts with '.'");
1538 l = findLabel(tstr);
1539 if (l != NULL) fatal("duplicate argument: '%s'", l->name);
1540 l = addLabel(tstr);
1541 l->type = LB_SVAR;
1542 l->value = 666;
1543 ++procargs;
1544 if (nextToken() != ',') break;
1546 a = labels; //HACK!
1547 // fix values
1548 // -1: return address, -2: last arg
1549 for (spt = -2, l = a; l->type == LB_SVAR; l = l->next) {
1550 l->value = spt;
1551 --spt;
1555 spt = -1; // first local
1556 while (token == TK_LABELDEF && (strcmp(tstr, "local") == 0 || strcmp(tstr, "locals") == 0)) {
1557 for (;;) {
1558 if (nextToken() != TK_ID) fatal("identifier expected");
1559 if (tstr[0] != '.') fatal("local variable name must starts with '.'");
1560 l = findLabel(tstr);
1561 if (l != NULL) fatal("duplicate local: '%s'", l->name);
1562 l = addLabel(tstr);
1563 l->type = LB_SVAR;
1564 l->value = spt--;
1565 ++proclocals;
1566 // fix args
1567 for (l = a; l != NULL && l->type == LB_SVAR; l = l->next) --(l->value);
1568 if (nextToken() != ',') break;
1572 if (proclocals > 0) {
1573 // allocate stack
1574 OperandInfo op0;
1576 setIntOperand(&op0, -proclocals);
1577 emitInstruction(VM_POP, &op0, NULL, NULL);
1582 static void parseEndP (void) {
1583 if (!procname[0]) fatal("'endp' without 'proc'");
1584 if (nextToken() != TK_ID) fatal("identifier expected");
1585 if (strcmp(procname, tstr) != 0) fatal("endp for '%s' in proc '%s'", tstr, procname);
1586 //if (!lastWasReturn) fatal("no 'return' in proc");
1587 nextToken();
1588 procname[0] = 0;
1589 freeLocalLabels(0);
1593 static void doReturn (void) {
1594 OperandInfo op0, op1, op2;
1596 if (!procname[0]) fatal("'return' without 'proc'");
1597 lastWasReturn = 1;
1598 op0.var = op1.var = op2.var = -1;
1599 if (hasOperand()) getOperand(&op2, 0); // result
1600 setIntOperand(&op0, proclocals);
1601 setIntOperand(&op1, procargs);
1602 emitInstruction(VM_RET, &op0, &op1, &op2);
1606 static void parseLabel (int gotext) {
1607 LabelInfo *l;
1609 if (gotext && tstr[0] == '.') fatal("can't declare local extern label: '%s'", tstr);
1610 if (strcmp(tstr, ".") == 0) {
1611 newTempLabel();
1612 nextToken();
1613 return;
1615 if (tstr[0] != '.' && tstr[0] != '@') {
1616 if (!gotext) {
1617 if (procname[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname);
1619 freeLocalLabels(0); // vars and code
1621 if (tstr[0] == '@') {
1622 char *d = tstr;
1624 while (*d++) d[-1] = d[0];
1625 d[-1] = 0;
1627 l = findLabel(tstr);
1628 if (l != NULL) {
1629 if (gotext) {
1630 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1631 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
1633 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate label: '%s'", tstr);
1634 fixupLabelRefs(l, pc);
1635 } else {
1636 l = addLabel(tstr);
1637 l->type = LB_CODE;
1638 l->value = pc;
1640 if (gotext) l->ext = -1;
1641 nextToken();
1645 static void parseDW (void) {
1646 for (;;) {
1647 LabelInfo *l = NULL;
1648 nextToken();
1649 if (token == TK_ID) {
1650 l = findLabel(tstr);
1652 if (l == NULL) {
1653 l = addLabel(tstr);
1654 l->type = LB_CODE;
1655 l->value = -1;
1656 //fatal("unknown label: '%s'", tstr);
1658 tint = l->value;
1659 } else if (token != TK_NUM) {
1660 fatal("number expected");
1662 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1663 emitByte(tint&0xff);
1664 emitByte((tint>>8)&0xff);
1665 if (l != NULL) {
1666 addLabelRef(l, pc-2);
1667 addExternRef(l, pc-2, 2);
1668 if (l->ext >= 0 && l->name[0] != '.') {
1669 switch (l->type) {
1670 case LB_GVAR: addGVarFixup(pc-2); break;
1671 case LB_TVAR: addTVarFixup(pc-2); break;
1675 if (nextToken() != ',') break;
1680 // terminator eaten
1681 static void parseAndPutString (int qch) {
1682 for (;;) {
1683 int ch = nextChar();
1685 //printf("[%c] [%c]\n", ch, qch);
1686 if (ch == EOF) fatal("unterminated string");
1687 if (qch == '"') {
1688 if (ch == qch) break;
1689 if (ch == '\\') {
1690 int n;
1692 ch = nextChar();
1693 if (ch == EOF) fatal("invalid escape");
1694 switch (ch) {
1695 case 'a': emitByte('\a'); break;
1696 case 'b': emitByte('\b'); break;
1697 case 'e': emitByte('\x1b'); break;
1698 case 'f': emitByte('\f'); break;
1699 case 'n': emitByte('\n'); break;
1700 case 'r': emitByte('\r'); break;
1701 case 't': emitByte('\t'); break;
1702 case 'v': emitByte('\v'); break;
1703 case '"': case '\'': case '\\': case ' ': emitByte(ch); break;
1704 case 'x':
1705 n = digit(nextChar(), 16);
1706 if (n < 0) fatal("invalid hex escape");
1707 ch = nextChar();
1708 if (ch == EOF) fatal("invalid hex escape");
1709 if (digit(ch, 16) >= 0) {
1710 n = n*16+digit(ch, 16);
1711 } else {
1712 ungetChar(ch);
1714 emitByte(n);
1715 break;
1716 default: fatal("invalid escape: '%c'", ch);
1718 } else {
1719 emitByte(ch);
1721 } else {
1722 if (ch == qch) {
1723 ch = nextChar();
1724 if (ch == EOF) return;
1725 if (ch == qch) {
1726 emitByte(ch);
1727 continue;
1729 ungetChar(ch);
1730 break;
1731 } else {
1732 emitByte(ch);
1739 static void parseDAscii (int zeroend) {
1740 for (;;) {
1741 LabelInfo *l;
1743 nextToken();
1744 if (token == TK_EOF) break;
1745 switch (token) {
1746 case '"': tokenWantFileName = 1; parseAndPutString(token); tokenWantFileName = 0; break;
1747 case '\'': tokenWantFileName = 1; parseAndPutString(token); tokenWantFileName = 0; break;
1748 case TK_ID:
1749 l = findLabel(tstr);
1751 if (l == NULL) fatal("unknown label: '%s'", tstr);
1752 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1753 l->used = 1;
1754 addExternRef(l, pc, 1);
1755 if (l->ext >= 0 && l->name[0] != '.') {
1756 switch (l->type) {
1757 case LB_GVAR: addGVarFixup(pc); break;
1758 case LB_TVAR: addTVarFixup(pc); break;
1761 tint = l->value;
1762 // fallthru
1763 case TK_NUM:
1764 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1765 emitByte(tint&0xff);
1766 break;
1767 default:
1768 fatal("number expected");
1770 if (nextToken() != ',') break;
1772 if (zeroend) emitByte(0);
1776 static void parsePublics (void) {
1777 for (;;) {
1778 LabelInfo *l;
1780 nextToken();
1781 if (token == TK_LABELDEF) {
1782 parseLabel(0);
1783 break;
1785 if (token != TK_ID) fatal("identifier expected");
1786 if (tstr[0] == '.') fatal("invalid label name: '%s'", tstr);
1787 l = findLabel(tstr);
1788 if (l != NULL) {
1789 l->ext = 1;
1790 } else {
1791 l = addLabel(tstr);
1792 l->ext = 1;
1793 l->type = LB_CODE;
1794 l->value = -1;
1796 if (nextToken() != ',') break;
1801 static void parseMacroDef (void) {
1802 MacroDef *mc;
1803 int c;
1804 char *text = NULL;
1805 int tpos = 0, tsize = 0;
1807 void addChar (int c) {
1808 if (tpos+1 > tsize) {
1809 int newsz = tsize+1024;
1810 char *nn = realloc(text, newsz);
1812 if (nn == NULL) fatal("out of memory");
1813 text = nn;
1814 tsize = newsz;
1816 text[tpos++] = c;
1819 if (nextToken() != TK_ID) fatal("macro name expected");
1820 if (tstr[0] == '.') fatal("macros can't be local: '%s'", tstr);
1821 for (mc = macros; mc != NULL; mc = mc->next) if (strcmp(mc->name, tstr) == 0) fatal("macros can't be redefined: '%s'", tstr);
1822 if ((mc = calloc(1, sizeof(MacroDef))) == NULL) fatal("out of memory");
1823 mc->next = macros;
1824 if ((mc->name = strdup(tstr)) == NULL) fatal("out of memory");
1825 macros = mc;
1826 // now parse macro args
1827 c = nextChar();
1828 ungetChar(c);
1829 skipSpaces(0); // no newlines allowed here
1830 c = nextChar();
1831 if (c != '\n') {
1832 // has args
1833 ungetChar(c);
1834 for (;;) {
1835 if (nextToken() != TK_ID) fatal("invalid macro argument definition");
1836 if (tstr[0] == '.') fatal("macro argument name must not start with dot: '%s'", tstr);
1837 addMacroArgDef(mc, tstr);
1838 skipSpaces(1);
1839 c = nextChar();
1840 if (c != ',') { ungetChar(c); break; }
1843 // now parse macro text
1844 for (;;) {
1845 int c1;
1847 c = nextChar();
1848 switch (c) {
1849 case EOF: fatal("incomplete macro definition: '%s'", mc->name);
1850 case '\n': // check for macro end
1851 addChar(c);
1852 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1853 if (c != 'e') { ungetChar(c); break; }
1854 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1855 if (c != 'n') { ungetChar(c); addChar('e'); break; }
1856 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1857 if (c != 'd') { ungetChar(c); addChar('e'); addChar('n'); break; }
1858 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1859 if (c != 'm') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); break; }
1860 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1861 if (c != 'a') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); break; }
1862 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1863 if (c != 'c') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); break; }
1864 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1865 if (c != 'r') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); break; }
1866 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1867 if (c != 'o') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); break; }
1868 c = nextChar();
1869 if (c == EOF) goto macro_complete;
1870 if (!isspace(c) && c != ';' && c != '/') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); addChar('o'); break; }
1871 else {
1872 skipSpaces(0); // no EOLs
1873 c = nextChar();
1874 if (c != '\n') fatal("invalid endmacro: '%s' (%d)", mc->name, c);
1875 goto macro_complete;
1877 break;
1878 case '"': // string
1879 addChar(c);
1880 tokenWantFileName = 1;
1881 for (;;) {
1882 c = nextChar();
1883 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1884 addChar(c);
1885 if (c == '"') break;
1886 if (c == '\\') {
1887 c = nextChar();
1888 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1889 addChar(c);
1892 tokenWantFileName = 0;
1893 break;
1894 case '\'': // string
1895 addChar(c);
1896 tokenWantFileName = 1;
1897 for (;;) {
1898 c = nextChar();
1899 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1900 addChar(c);
1901 if (c == '\'') {
1902 c = nextChar();
1903 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1904 if (c != '\'') { ungetChar(c); break; }
1905 addChar(c);
1908 tokenWantFileName = 0;
1909 break;
1910 case ';': // comment
1911 one_line_comment:
1912 addChar('\n');
1913 for (;;) {
1914 c = nextChar();
1915 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1916 if (c == '\n') break;
1918 break;
1919 case '/':
1920 c1 = nextChar();
1921 if (c1 == '/') goto one_line_comment; // comment
1922 if (c1 == '*') {
1923 // multiline comment
1924 for (;;) {
1925 c = nextChar();
1926 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1927 if (c == '*') {
1928 c = nextChar();
1929 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1930 if (c == '/') break;
1933 } else if (c1 == EOF) {
1934 fatal("incomplete macro definition: '%s'", mc->name);
1935 } else {
1936 addChar(c);
1937 addChar(c1);
1939 break;
1940 default:
1941 addChar(c);
1942 break;
1945 macro_complete:
1946 addChar('\0');
1947 mc->text = text;
1948 nextToken();
1952 static void parseMacroInvocation (MacroDef *mc) {
1953 MStrListItem *args = NULL;
1955 if (mc->argnames != NULL) {
1956 nextToken(); // skip macro name
1957 // collect macro arguments
1958 for (MStrListItem *a = mc->argnames; a != NULL; a = a->next) {
1959 int pos, c;
1961 switch (token) {
1962 case TK_EOF: fatal("macro argument expected: '%s'", mc->name);
1963 case TK_LABELDEF: fatal("macro argument can't be label: '%s'", mc->name);
1964 case TK_NUM: sprintf(tstr, "%d", tint); break;
1965 case TK_ID: break; // ok
1966 case '"': // string
1967 pos = 0;
1968 tstr[pos++] = token;
1969 tokenWantFileName = 1;
1970 for (;;) {
1971 c = nextChar();
1972 if (c == EOF) fatal("incomplete macro argument: '%s'", mc->name);
1973 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1974 tstr[pos++] = c;
1975 if (c == '"') break;
1976 if (c == '\\') {
1977 c = nextChar();
1978 if (c == EOF) fatal("incomplete macro argument: '%s'", mc->name);
1979 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1980 tstr[pos++] = c;
1983 tokenWantFileName = 0;
1984 tstr[pos] = 0;
1985 break;
1986 case '\'': // string
1987 pos = 0;
1988 tstr[pos++] = token;
1989 tokenWantFileName = 1;
1990 for (;;) {
1991 c = nextChar();
1992 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1993 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1994 tstr[pos++] = c;
1995 if (c == '\'') {
1996 c = nextChar();
1997 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1998 if (c != '\'') { ungetChar(c); break; }
1999 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
2000 tstr[pos++] = c;
2003 tokenWantFileName = 0;
2004 tstr[pos] = 0;
2005 break;
2006 default:
2007 if (token >= TK_VM_OPERATOR) break;
2008 pos = 0;
2009 tstr[pos++] = token;
2010 for (;;) {
2011 c = nextChar();
2012 if (c == EOF) break;//fatal("incomplete macro definition: '%s'", mc->name);
2013 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
2014 //TODO: better comment parsing
2015 if (c == ',' || c == ';' || c == '/' || c == '\n') { ungetChar(c); break; }
2016 tstr[pos++] = c;
2018 while (isspace(tstr[pos-1])) --pos;
2019 tstr[pos] = 0;
2020 break;
2021 //fatal("invalid macro argument: '%s'", mc->name);
2023 //fprintf(stderr, "arg [%s]: [%s]\n", mc->argnames->macname, tstr);
2024 args = addMacroArg(args, mc->argnames->macname, tstr);
2025 if (a->next != NULL) {
2026 if (nextToken() != ',') fatal("macro argument expected: '%s'", mc->name);
2030 // arguments collected
2031 openMacro(mc, args);
2032 nextToken();
2036 static void parseLabelDef () {
2037 LabelInfo *l;
2039 if (nextToken() != TK_ID) fatal("label name expected");
2041 l = findLabel(tstr);
2042 if (strcmp(tstr, ".") == 0) fatal("can't declare temp label");
2044 if (l != NULL) {
2045 // new
2046 if (l->ext < 0) fatal("can't declare extern label: '%s'", tstr);
2047 if (l->type != LB_CODE || l->value >= 0) fatal("can't redeclare label: '%s'", tstr);
2048 //fixupLabelRefs(l, pc);
2049 } else {
2050 l = addLabel(tstr);
2051 l->type = LB_CODE;
2052 l->value = -1;
2055 if (nextToken() == '=') {
2056 nextToken();
2057 //fprintf(stderr, "token=%d\n", token);
2058 if (token == '$') {
2059 int ch;
2061 //TODO: allow simple repeated math (with labels too)
2062 l->value = pc;
2063 skipSpaces(0); // not newlines
2064 ch = nextChar();
2065 if (ch == '-' || ch == '+') {
2066 nextToken();
2067 if (token != TK_NUM) fatal("number expected");
2068 if (ch == '-') tint = -tint;
2069 l->value += tint;
2070 //fprintf(stderr, "pc=%d; tint=%d; value=%d\n", pc, tint, l->value);
2071 } else {
2072 if (ch != EOF && ch != '\n') fatal("invalid label definition: '%s'", l->name);
2074 } else if (token == TK_NUM) {
2075 l->value = tint;
2076 } else {
2077 fatal("invalid label definition: '%s'", l->name);
2079 nextToken();
2080 } else {
2081 l->value = pc;
2083 if (l->value < 0) l->value &= 0xffff;
2084 fixupLabelRefs(l, l->value);
2088 static void process (void) {
2089 int gotext = 0;
2090 LabelInfo *l = addLabel("retval");
2092 l->type = LB_TVAR;
2093 l->value = VM_VARS_SIZE-1;
2095 memset(&labelTempBack, 0, sizeof(LabelInfo));
2096 memset(&labelTempFwd, 0, sizeof(labelTempFwd));
2098 labelTempBack.type = labelTempFwd.type = LB_CODE;
2099 labelTempBack.value = labelTempFwd.value = -1;
2101 procname[0] = 0;
2102 lastWasReturn = 0;
2103 nextToken();
2104 while (token != TK_EOF) {
2105 int opc;
2107 if (token == TK_LABELDEF) {
2108 if (strcmp(tstr, "extern") == 0) {
2109 if (gotext) fatal("double 'extern'");
2110 gotext = 1;
2111 nextToken();
2112 continue;
2114 // new label or operator
2115 if (strcmp(tstr, "include") == 0) {
2116 parseInlcude();
2117 continue;
2118 } else if (strcmp(tstr, "defloc") == 0) {
2119 if (gotext) fatal("extern labels must not be locals");
2120 parseLocList();
2121 } else if (strcmp(tstr, "defgvar") == 0) {
2122 parseVarList(LB_GVAR, 0, gotext);
2123 } else if (strcmp(tstr, "defevar") == 0) { // extern global
2124 parseVarList(LB_GVAR, -1, gotext);
2125 } else if (strcmp(tstr, "deftvar") == 0) {
2126 freeLocalLabels(1); // only vars
2127 vtloc = VM_VARS_SIZE;
2128 parseVarList(LB_TVAR, 0, gotext);
2129 } else if (strcmp(tstr, "defetvar") == 0) {
2130 freeLocalLabels(1); // only vars
2131 vtloc = VM_VARS_SIZE;
2132 parseVarList(LB_TVAR, -1, gotext);
2133 } else if (strcmp(tstr, "deflvar") == 0) {
2134 if (gotext) fatal("extern labels must not be locals");
2135 parseVarList(LB_TVAR, 1, gotext);
2136 } else if (strcmp(tstr, "public") == 0) {
2137 if (gotext) fatal("invalid extern label declaration");
2138 parsePublics();
2139 } else if (strcmp(tstr, "proc") == 0 || strcmp(tstr, "eproc") == 0) {
2140 if (gotext) fatal("invalid extern label declaration");
2141 parseProc(tstr[0] == 'e');
2142 } else if (strcmp(tstr, "endp") == 0) {
2143 if (gotext) fatal("invalid extern label declaration");
2144 parseEndP();
2145 } else if (strcmp(tstr, "const") == 0 || strcmp(tstr, "econst") == 0) {
2146 parseConst(tstr[0] == 'e', gotext);
2147 } else if (strcmp(tstr, "db") == 0) {
2148 if (gotext) fatal("invalid extern label declaration");
2149 parseDAscii(0);
2150 } else if (strcmp(tstr, "dw") == 0) {
2151 if (gotext) fatal("invalid extern label declaration");
2152 parseDW();
2153 } else if (strcmp(tstr, "da") == 0) {
2154 if (gotext) fatal("invalid extern label declaration");
2155 parseDAscii(0);
2156 } else if (strcmp(tstr, "dz") == 0) {
2157 if (gotext) fatal("invalid extern label declaration");
2158 parseDAscii(1);
2159 } else if (strcmp(tstr, "macro") == 0) {
2160 if (gotext) fatal("macros can't be external");
2161 parseMacroDef();
2162 } else if (strcmp(tstr, "used") == 0) {
2163 if (gotext) fatal("'used' labels can't be external");
2164 parseUsedVarList();
2165 } else if (strcmp(tstr, "gvarbase") == 0) {
2166 if (gotext) fatal("'gvarbase' can't be external");
2167 if (nextToken() != TK_NUM) fatal("gvarbase: number expected");
2168 vglast = tint;
2169 nextToken();
2170 } else if (strcmp(tstr, "tvarbase") == 0) {
2171 if (gotext) fatal("'tvarbase' can't be external");
2172 if (nextToken() != TK_NUM) fatal("tvarbase: number expected");
2173 vtlast = tint;
2174 nextToken();
2175 } else if (strcmp(tstr, "label") == 0) {
2176 if (gotext) fatal("'label' can't be external");
2177 parseLabelDef();
2178 } else {
2179 parseLabel(gotext);
2181 gotext = 0;
2182 continue;
2183 } else {
2184 if (gotext) {
2185 LabelInfo *l;
2187 if (token != TK_ID) fatal("label declaration expected after 'extern'");
2188 if (tstr[0] == '.') fatal("extern label can't be local: '%s'", tstr);
2189 l = findLabel(tstr);
2190 if (l != NULL) {
2191 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
2192 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
2193 } else {
2194 l = addLabel(tstr);
2195 l->type = LB_CODE;
2196 l->value = 0;
2197 l->ext = -1;
2199 nextToken();
2200 continue;
2203 // check for macro invocation
2204 if (token == TK_ID) {
2205 for (MacroDef *mc = macros; mc != NULL; mc = mc->next) {
2206 if (strcmp(mc->name, tstr) == 0) {
2207 parseMacroInvocation(mc);
2208 continue;
2213 if (token < TK_VM_OPERATOR) {
2214 //fprintf(stderr, "%d: [%s]\n", token, tstr);
2215 fatal("mnemonics expected");
2217 opc = token;
2218 if (opc < 600) opc -= TK_VM_OPERATOR;
2219 nextToken();
2220 lastWasReturn = 0;
2221 switch (opc) {
2222 case VM_ADD:
2223 case VM_SUB:
2224 case VM_MUL:
2225 case VM_DIV:
2226 case VM_MOD:
2227 case VM_BOR:
2228 case VM_XOR:
2229 case VM_AND:
2230 doMath(opc);
2231 break;
2232 case VM_JEQ:
2233 case VM_JNE:
2234 case VM_JLT:
2235 case VM_JLE:
2236 case VM_JGT:
2237 case VM_JGE:
2238 doJXX(opc);
2239 break;
2240 case VM_JMP:
2241 doBranch(opc);
2242 break;
2243 case VM_END:
2244 doNoOperands(opc);
2245 break;
2246 case VM_BSR:
2247 doBSR(opc);
2248 break;
2249 case VM_NEW:
2250 doNew(opc);
2251 break;
2252 case VM_BRK:
2253 doNoOperands(opc);
2254 break;
2255 case VM_SET:
2256 doSet(opc);
2257 break;
2258 case VM_GET:
2259 doGet(opc);
2260 break;
2261 case VM_PSH:
2262 doPush(opc);
2263 break;
2264 case VM_POP:
2265 doPop(opc);
2266 break;
2267 case VM_SWP:
2268 doSwap(opc);
2269 break;
2270 case VM_PCK:
2271 case VM_ROL:
2272 doPickRoll(opc);
2273 break;
2274 case VM_DPT:
2275 doDepth(opc);
2276 break;
2277 case VM_TID:
2278 doTId(opc);
2279 break;
2280 case VM_KIL:
2281 case VM_SUS:
2282 case VM_RES:
2283 doKillSusRes(opc);
2284 break;
2285 case VM_STA:
2286 doSta(opc);
2287 break;
2288 case VM_RXC:
2289 doRXC(opc);
2290 break;
2291 case VM_WXC:
2292 doWXC(opc);
2293 break;
2294 case VM_RST:
2295 doRST(opc);
2296 break;
2297 case VM_MGF:
2298 case VM_MGB:
2299 doMap(opc, 1);
2300 break;
2301 case VM_MSF:
2302 case VM_MSB:
2303 doMap(opc, 0);
2304 break;
2305 case VM_RET:
2306 if (procname[0]) doReturn(); else doRet(opc);
2307 break;
2308 case VMX_DRP:
2309 doDrop(VM_POP);
2310 break;
2311 case VMX_DUP:
2312 doNoOperands(VM_PSH);
2313 break;
2314 case VMX_RTN:
2315 doRet(opc);
2316 break;
2317 case VMX_RETURN:
2318 doReturn();
2319 break;
2320 case VMX_SAJ:
2321 doSAj(opc);
2322 break;
2323 default:
2324 fatal("not yet");
2327 if (procname[0]) fatal("'proc' without 'endp': '%s'", procname);
2331 ////////////////////////////////////////////////////////////////////////////////
2332 static int cfWriteByte (FILE *fl, int value) {
2333 unsigned char b;
2335 b = (value&0xff)^SECRET;
2336 if (fwrite(&b, 1, 1, fl) != 1) return -1;
2337 return 0;
2341 static int cfWriteWord (FILE *fl, int value) {
2342 if (cfWriteByte(fl, value) != 0) return -1;
2343 if (cfWriteByte(fl, value>>8) != 0) return -1;
2344 return 0;
2348 static int cfWriteCode (FILE *fl) {
2349 for (int f = 0; f < pc; ++f) if (cfWriteByte(fl, vmcode[f]) != 0) return -1;
2350 return 0;
2354 static int cfWriteRels (FILE *fl) {
2355 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) {
2356 if (cfWriteWord(fl, r->pc) != 0) return -1;
2358 return 0;
2362 static int cfWritePublicLabels (FILE *fl) {
2363 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2364 if (l->ext >= 0 && l->name[0]) {
2365 int len = strlen(l->name);
2367 if (cfWriteByte(fl, l->type|(l->ext>0?0x80:0)) != 0) return -1;
2369 switch (l->type) {
2370 case LB_GVAR:
2371 case LB_TVAR:
2372 if (cfWriteByte(fl, l->value) != 0) return -1;
2373 break;
2374 case LB_SVAR:
2375 case LB_CODE:
2376 case LB_CONST:
2377 if (cfWriteWord(fl, l->value) != 0) return -1;
2378 break;
2379 default:
2380 abort();
2383 if (len > 255) len = 255;
2384 if (cfWriteByte(fl, len) != 0) return -1;
2385 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
2388 return 0;
2392 static int cfWriteExtLabels (FILE *fl) {
2393 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2394 if (l->ext < 0 && l->name[0] && l->refs != NULL) {
2395 int len = strlen(l->name), rcnt = 0;
2397 if (cfWriteByte(fl, l->type) != 0) return -1;
2398 if (len > 255) len = 255;
2399 if (cfWriteByte(fl, len) != 0) return -1;
2400 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
2402 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) ++rcnt;
2403 if (cfWriteWord(fl, rcnt) != 0) return -1;
2404 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) {
2405 if (cfWriteByte(fl, r->size) != 0) return -1;
2406 if (cfWriteWord(fl, r->pc) != 0) return -1;
2410 return 0;
2414 static int cfWriteVarFixups (FILE *fl, const VarFixup *list) {
2415 int cnt = 0;
2417 for (const VarFixup *f = list; f != NULL; f = f->next) ++cnt;
2418 if (cfWriteWord(fl, cnt) != 0) return -1;
2419 for (const VarFixup *f = list; f != NULL; f = f->next) {
2420 if (cfWriteWord(fl, f->pc) != 0) return -1;
2422 return 0;
2426 static int writeCodeFile (FILE *fl) {
2427 static const char *sign = "AVM2";
2428 int lcnt = 0, elcnt = 0;
2429 int rcnt = 0;
2431 relrefs = lrefRemoveDups(relrefs);
2432 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) ++rcnt;
2433 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2434 if (l->name[0]) {
2435 if (l->ext >= 0) ++lcnt;
2436 if (l->ext < 0) {
2437 l->refs = lrefRemoveDups(l->refs);
2438 if (l->refs != NULL) ++elcnt;
2442 fprintf(stderr, "%d bytes of code, %d public labels, %d fixups, %d externs; maxgvar: %d, maxtvar: %d\n", pc, lcnt, rcnt, elcnt, vglast, vtlast);
2444 if (fwrite(sign, 4, 1, fl) != 1) return -1;
2445 // code size
2446 if (cfWriteWord(fl, pc) != 0) return -1;
2447 // number of fixups
2448 if (cfWriteWord(fl, rcnt) != 0) return -1;
2449 // number of extern labels
2450 if (cfWriteWord(fl, elcnt) != 0) return -1;
2451 // number of labels
2452 if (cfWriteWord(fl, lcnt) != 0) return -1;
2453 // last used global
2454 if (cfWriteWord(fl, vglast) != 0) return -1;
2455 // last used thread local
2456 if (cfWriteWord(fl, vtlast) != 0) return -1;
2458 if (cfWriteCode(fl) != 0) return -1;
2459 if (cfWriteRels(fl) != 0) return -1;
2460 if (cfWriteExtLabels(fl) != 0) return -1;
2461 if (cfWritePublicLabels(fl) != 0) return -1;
2462 if (cfWriteVarFixups(fl, gvfixes) != 0) return -1;
2463 if (cfWriteVarFixups(fl, tvfixes) != 0) return -1;
2465 return 0;
2469 ////////////////////////////////////////////////////////////////////////////////
2470 #ifdef _WIN32
2471 # include "cmdline.c"
2472 #endif
2475 ////////////////////////////////////////////////////////////////////////////////
2476 int main (int argc, char *argv[]) {
2477 #ifdef _WIN32
2478 cmdLineParse();
2479 argc = k8argc;
2480 argv = k8argv;
2481 #endif
2483 for (int f = 1; f < argc; ++f) {
2484 if (strcmp(argv[f], "-Wno") == 0) {
2485 optWarnings = 0;
2486 for (int c = f+1; c < argc; ++c) argv[c-1] = argv[c];
2487 argv[--argc] = NULL;
2488 --f;
2489 continue;
2493 if (argc != 3) {
2494 fprintf(stderr, "usage: awasm infile outfile\n");
2495 return 1;
2497 openFile(argv[1]);
2498 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
2499 process();
2500 while (ictx != NULL) closeFile();
2501 freeLocalLabels(0); // vars and code
2502 checkLabels();
2503 //if (argc > 3) dumpGlobalVars(argv[3]);
2505 FILE *fo = fopen(argv[2], "wb");
2506 int res;
2508 if (fo == NULL) { fprintf(stderr, "FATAL: can't create output file: '%s'\n", argv[2]); return 1; }
2509 res = writeCodeFile(fo);
2510 if (fclose(fo) != 0) res = -1;
2511 if (res != 0) {
2512 fprintf(stderr, "FATAL: error writing output file: '%s'\n", argv[2]);
2513 unlink(argv[2]);
2514 return 1;
2517 freeLabels();
2518 freeLabelRefList(relrefs);
2519 freeMacroList();
2520 return 0;