awasm: new code file format; fixes for (g|t)vars
[awish.git] / src / awasm.c
blob974f1a6e8043a709ea12b6408085b794b1913a4a
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 "vm.h"
27 ////////////////////////////////////////////////////////////////////////////////
28 #define SECRET (42)
31 ////////////////////////////////////////////////////////////////////////////////
32 static const char *ltnames[] = {
33 "GVAR",
34 "TVAR",
35 "SVAR",
36 "CODE",
37 "CONST"
41 ////////////////////////////////////////////////////////////////////////////////
42 #define MAX_TOKEN_LENGTH (256)
44 enum {
45 TK_EOF = -1,
46 TK_ID = 256,
47 TK_NUM,
48 TK_LABELDEF,
50 TK_VM_OPERATOR = 400
54 enum {
55 VMX_DRP = 600,
56 VMX_DUP,
57 VMX_RTN,
58 VMX_RETURN
62 ////////////////////////////////////////////////////////////////////////////////
63 static int optWarnings = 1;
66 ////////////////////////////////////////////////////////////////////////////////
67 static uint8_t vmcode[65536];
68 static int pc = 0;
71 ////////////////////////////////////////////////////////////////////////////////
72 typedef struct MStrListItem {
73 struct MStrListItem *next;
74 char *macname;
75 char *newname;
76 } MStrListItem;
79 typedef struct InputContext {
80 struct InputContext *prev;
81 FILE *fl;
82 char *fname;
83 int lineno;
84 int ucnt;
85 int uca[4];
86 int wasEOF;
87 char *text; // not NULL: macro argument expansion
88 struct MacroDef *macro; // !NULL: we are inside macro expanding
89 int tpos; // position in macro or in text
90 MStrListItem *macnames;
91 MStrListItem *macargs;
92 } InputContext;
95 static InputContext *ictx = NULL;
96 static int token;
97 static char tstr[MAX_TOKEN_LENGTH+1];
98 static int tint;
99 static int incLevel = 0;
102 ////////////////////////////////////////////////////////////////////////////////
103 typedef struct MacroDef {
104 struct MacroDef *next;
105 char *name;
106 char *text;
107 int argc;
108 MStrListItem *argnames;
109 } MacroDef;
112 static MacroDef *macros = NULL;
115 static void freeMStrList (MStrListItem *list) {
116 while (list != NULL) {
117 MStrListItem *c = list;
119 list = c->next;
120 if (c->newname != NULL) free(c->newname);
121 if (c->macname != NULL) free(c->macname);
122 free(c);
127 static void freeMacroList (void) {
128 while (macros != NULL) {
129 MacroDef *mc = macros;
131 macros = mc->next;
132 freeMStrList(mc->argnames);
133 if (mc->name) free(mc->name);
134 if (mc->text) free(mc->text);
135 free(mc);
140 ////////////////////////////////////////////////////////////////////////////////
141 static __attribute__((__noreturn__)) __attribute__((format(printf, 1, 2))) void fatal (const char *fmt, ...) {
142 va_list ap;
144 while (ictx != NULL && ictx->fl == NULL) ictx = ictx->prev;
146 if (ictx != NULL) {
147 fprintf(stderr, "FATAL (line %d, file '%s'): ", ictx->lineno, ictx->fname);
148 } else {
149 fprintf(stderr, "FATAL: ");
151 va_start(ap, fmt);
152 vfprintf(stderr, fmt, ap);
153 va_end(ap);
154 fprintf(stderr, "\n");
155 exit(1);
159 ////////////////////////////////////////////////////////////////////////////////
160 static void addMacroArgDef (MacroDef *mc, const char *name) {
161 MStrListItem *p = NULL, *c;
163 for (MStrListItem *ml = mc->argnames; ml != NULL; p = ml, ml = ml->next) {
164 if (strcmp(ml->macname, name) == 0) fatal("duplicate macro argument name: '%s'", name);
166 if ((c = calloc(1, sizeof(MStrListItem))) == NULL) fatal("out of memory");
167 if ((c->macname = strdup(name)) == NULL) fatal("out of memory");
169 if (p != NULL) {
170 p->next = c;
171 } else {
172 mc->argnames = c;
174 ++(mc->argc);
178 static MStrListItem *genUniqueMacName (MStrListItem *list, const char *macname) {
179 MStrListItem *res;
180 static int count = 0;
182 for (res = list; res != NULL; res = res->next) if (strcmp(res->macname, macname) == 0) return list;
183 if ((res = calloc(1, sizeof(MStrListItem))) == NULL) fatal("out of memory");
184 if ((res->macname = strdup(macname)) == NULL) fatal("out of memory");
185 if ((res->newname = calloc(1, 128)) == NULL) fatal("out of memory");
186 sprintf(res->newname, ". lmd #%d", count++);
187 res->next = list;
188 return res;
192 static MStrListItem *addMacroArg (MStrListItem *args, const char *argname, const char *argtext) {
193 MStrListItem *res;
195 if ((res = calloc(1, sizeof(MStrListItem))) == NULL) fatal("out of memory");
196 if ((res->macname = strdup(argname)) == NULL) fatal("out of memory");
197 if ((res->newname = strdup(argtext)) == NULL) fatal("out of memory");
198 res->next = args;
199 return res;
203 ////////////////////////////////////////////////////////////////////////////////
204 static void openFile (const char *fname) {
205 InputContext *ic = calloc(1, sizeof(InputContext));
207 if (ic == NULL) fatal("out of memory");
208 ic->fl = fopen(fname, "r");
209 if (ic->fl == NULL) fatal("can't open file: '%s'", fname);
210 ic->prev = ictx;
211 ic->fname = strdup(fname);
212 #ifdef _WIN32
213 for (char *t = ic->fname; *t; ++t) if (*t == '\\') *t = '/';
214 #endif
215 ic->lineno = 1;
216 ictx = ic;
217 for (int f = 0; f < incLevel; ++f) fputc(' ', stderr);
218 ++incLevel;
219 fprintf(stderr, "compiling: %s\n", ic->fname);
223 static void openText (const char *text) {
224 InputContext *ic = calloc(1, sizeof(InputContext));
226 if (ic == NULL) fatal("out of memory");
227 ic->prev = ictx;
228 ic->lineno = 1;
229 if ((ic->text = strdup(text)) == NULL) fatal("out of memory");
230 ictx = ic;
234 static void openMacro (MacroDef *mac, MStrListItem *args) {
235 InputContext *ic = calloc(1, sizeof(InputContext));
237 if (ic == NULL) fatal("out of memory");
238 ic->prev = ictx;
239 ic->lineno = 1;
240 ic->macro = mac;
241 ic->macargs = args;
242 ictx = ic;
246 static void closeFile (void) {
247 if (ictx != NULL) {
248 InputContext *ic = ictx;
250 ictx = ic->prev;
251 if (ic->fl != NULL) {
252 fclose(ic->fl);
253 --incLevel;
255 if (ic->fname) free(ic->fname);
256 if (ic->text != NULL) free(ic->text);
257 freeMStrList(ic->macnames);
258 freeMStrList(ic->macargs);
259 free(ic);
264 static inline int inText (void) {
265 return (ictx != NULL && ictx->text != NULL);
269 ////////////////////////////////////////////////////////////////////////////////
270 static int nextToken (void);
272 static void transformToken (void) {
273 // check if this is macroarg
274 if (!inText() && (token == TK_LABELDEF || token == TK_ID)) {
275 for (InputContext *ic = ictx; ic != NULL; ic = ic->prev) {
276 for (MStrListItem *ml = ic->macargs; ml != NULL; ml = ml->next) {
277 if (strcmp(tstr, ml->macname) == 0) {
278 if (token != TK_ID) fatal("macro argument redefinitions are prohibited");
279 //printf("marg [%s]: [%s]\n", ml->macname, ml->newname);
280 openText(ml->newname);
281 nextToken();
282 return;
287 // check macro labels
288 if (!inText() && tstr[0] == '$' && (token == TK_LABELDEF || token == TK_ID)) {
289 for (InputContext *ic = ictx; ic != NULL; ic = ic->prev) {
290 for (MStrListItem *ml = ic->macnames; ml != NULL; ml = ml->next) {
291 if (strcmp(tstr, ml->macname) == 0) { strcpy(tstr, ml->newname); return; }
295 // not found or in text; generate new macro name
296 if (!inText() && tstr[0] == '$' && (token == TK_LABELDEF || token == TK_ID)) {
297 for (InputContext *ic = ictx; ic != NULL; ic = ic->prev) {
298 if (ic->macro != NULL) {
299 strcpy(tstr, (ic->macnames = genUniqueMacName(ic->macnames, tstr))->newname);
300 break;
307 static void ungetChar (int c) {
308 if (ictx != NULL) {
309 if (ictx->ucnt >= 4) fatal("too many unread chars");
310 ictx->uca[ictx->ucnt++] = c;
315 static int tokenWantFileName = 0;
317 static int nextChar (void) {
318 int c;
320 if (ictx == NULL) return EOF;
321 if (ictx->ucnt > 0) {
322 c = ictx->uca[--ictx->ucnt];
323 } else {
324 if (ictx->wasEOF) {
325 c = EOF;
326 } else if (ictx->fl != NULL) {
327 c = fgetc(ictx->fl);
328 if (c == 0) c = ' ';
329 } else if (ictx->text != NULL) {
330 c = ictx->text[ictx->tpos++];
331 if (c == 0) { --(ictx->tpos); c = EOF; }
332 } else {
333 c = ictx->macro->text[ictx->tpos++];
334 if (c == 0) { --(ictx->tpos); c = EOF; }
336 if (c == EOF) {
337 if (ictx->wasEOF) {
338 closeFile();
339 c = (ictx==NULL ? EOF : '\n');
340 } else {
341 ictx->wasEOF = 1;
342 c = ' ';
344 } else {
345 if (c == '\n') ++(ictx->lineno);
348 if (!tokenWantFileName) {
349 if (c >= 'A' && c <= 'Z') c += 32; // tolower
351 return c;
355 static void skipSpaces (int allowNL) {
356 for (;;) {
357 int c = nextChar();
359 if (c == EOF) return;
360 if (c == '\n' && !allowNL) { ungetChar(c); return; }
361 if (c == ';') {
362 do { c = nextChar(); } while (c != EOF && c != '\n');
363 if (!allowNL) { if (c != EOF) ungetChar(c); return; }
364 continue;
366 if (c == '/') {
367 int c1 = nextChar();
369 if (c1 == '/') {
370 do { c = nextChar(); } while (c != EOF && c != '\n');
371 if (!allowNL) { if (c != EOF) ungetChar(c); return; }
372 continue;
374 if (c1 == '*') {
375 int wasNL = 0;
377 for (;;) {
378 c = nextChar();
379 if (c == EOF) break;
380 if (c == '\n') wasNL = 1;
381 if (c == '*') {
382 c = nextChar();
383 if (c == '\n') wasNL = 1;
384 if (c == '/') break;
385 if (c == EOF) fatal("unterminated comment");
388 if (!allowNL && wasNL) { ungetChar('\n'); return; }
389 continue;
391 ungetChar(c1);
392 ungetChar(c);
393 break;
395 if (c > 32) { ungetChar(c); break; }
400 static int digit (int c, int base) {
401 if (c == EOF) return -1;
402 if (c >= 'a' && c <= 'z') c -= 32;
403 if (c > '9' && c < 'A') return -1;
404 if (c > '9') c -= 7;
405 c -= '0';
406 if (c < 0 || c >= base) return -1;
407 return c;
411 static void getNumber (int c) {
412 int base = 10;
414 token = TK_NUM;
415 tint = 0;
416 if (c == '0') {
417 c = nextChar();
418 if (c == EOF) return;
419 if (!isdigit(c)) {
420 switch (c) {
421 case 'b': case 'B': base = 2; break;
422 case 'o': case 'O': base = 8; break;
423 case 'd': case 'D': base = 10; break;
424 case 'x': case 'X': base = 16; break;
425 default:
426 if (isalpha(c)) fatal("invalid number");
427 if (c != EOF) ungetChar(c);
428 return;
430 c = nextChar();
431 if (digit(c, base) < 0) fatal("invalid number");
435 for (;;) {
436 int d = digit(c, base);
438 if (d < 0) break;
439 tint = (tint*base)+d;
440 if (tint > 32767) fatal("number constant too big");
441 c = nextChar();
443 if (c != EOF && isalpha(c)) fatal("invalid number");
444 if (c != EOF) ungetChar(c);
448 static int nextToken (void) {
449 int c;
451 memset(tstr, 0, sizeof(tstr));
452 tint = 0;
453 token = TK_EOF;
454 skipSpaces(1);
455 if (ictx == NULL) return TK_EOF;
456 c = nextChar();
457 if (c == EOF) return TK_EOF;
458 if (c >= 127) fatal("invalid char (%d)", c);
459 tstr[0] = c;
460 tstr[1] = 0;
462 if (isalpha(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
463 // id or label
464 int f = 1;
466 for (;;) {
467 c = nextChar();
468 if (isalnum(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
469 if (f >= MAX_TOKEN_LENGTH-1) fatal("identifier too long");
470 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
471 tstr[f++] = c;
472 } else {
473 //if (c != ';' && c > 32) fatal("invalid identifier");
474 break;
477 tstr[f] = 0;
478 if (!isalnum(tstr[0]) && !tstr[1]) {
479 if (c != EOF) ungetChar(c);
480 token = tstr[0];
481 return token;
483 token = TK_ID;
484 // label definition?
485 if (!tokenWantFileName) {
486 if (c == ':') {
487 token = TK_LABELDEF;
488 } else {
489 if (c != EOF) ungetChar(c);
491 // vm mnemonics?
492 if (f == 3) {
493 for (f = 0; vmOpNames[f]; ++f) {
494 if (strcmp(tstr, vmOpNames[f]) == 0) {
495 if (token == TK_LABELDEF) fatal("invalid label: '%s'", tstr);
496 token = TK_VM_OPERATOR+f;
497 break;
500 if (token < TK_VM_OPERATOR) {
501 if (strcmp(tstr, "drp") == 0) token = VMX_DRP;
502 else if (strcmp(tstr, "dup") == 0) token = VMX_DUP;
503 else if (strcmp(tstr, "rtn") == 0) token = VMX_RTN;
505 } else if (strcmp(tstr, "return") == 0) {
506 token = VMX_RETURN;
508 if (strcmp(tstr, "@@") == 0) strcpy(tstr, "."); // special label
509 transformToken(); // macro transformations
511 } else if (c == '-' || c == '+') {
512 int neg = (c=='-');
514 token = c;
515 if ((c = nextChar()) != EOF) {
516 if (isdigit(c)) {
517 getNumber(c);
518 if (neg) tint = -tint;
519 } else {
520 ungetChar(c);
523 } else if (isdigit(c)) {
524 // number
525 getNumber(c);
526 } else {
527 // delimiter
528 token = c;
530 return token;
534 ////////////////////////////////////////////////////////////////////////////////
535 typedef struct LabelRefInfo {
536 struct LabelRefInfo *next;
537 int pc;
538 int size;
539 } LabelRefInfo;
542 typedef struct LabelInfo {
543 struct LabelInfo *next;
544 char *name;
545 int type; // LB_XXXX
546 int value; // code && <0: not defined yet (forward ref)
547 int ext; // extern (<0), public(>0), normal(0)
548 LabelRefInfo *fixes; // for undefined code labels
549 LabelRefInfo *refs; // for externals
550 int used;
551 } LabelInfo;
554 static LabelInfo *labels = NULL;
555 static LabelRefInfo *relrefs = NULL; // info for relocaions
556 static LabelRefInfo *refFwd = NULL; // @@f references (will be resolved on next @@)
557 static int prevTmpLabelPC = -1; // previous @@ PC (-1: not defined yet)
558 static LabelInfo labelTempBack, labelTempFwd;
560 static int vglast = 0;
561 static int vtlast = 0;
562 static int vtloc = VM_VARS_SIZE-1; // local thread (var 126 is used for internal thing)
565 typedef struct VarFixup {
566 struct VarFixup *next;
567 int pc;
568 } VarFixup;
571 static VarFixup *gvfixes = NULL;
572 static VarFixup *tvfixes = NULL;
575 static VarFixup *addVarFixup (VarFixup *list, int pc) {
576 VarFixup *res = calloc(1, sizeof(VarFixup));
578 if (res == NULL) fatal("out of memory");
579 res->next = list;
580 res->pc = pc;
581 return res;
585 static void addGVarFixup (int pc) {
586 gvfixes = addVarFixup(gvfixes, pc);
590 static void addTVarFixup (int pc) {
591 tvfixes = addVarFixup(tvfixes, pc);
595 static LabelRefInfo *lrefRemoveDups (LabelRefInfo *list) {
596 LabelRefInfo *p = NULL, *c = list;
598 list = NULL;
599 while (c != NULL) {
600 int dup = 0;
602 if (list != NULL) {
603 for (LabelRefInfo *r = list; r != c; r = r->next) if (r->pc == c->pc && r->size == c->size) { dup = 1; break; }
606 if (dup) {
607 // remove
608 LabelRefInfo *n = c->next;
610 if (p != NULL) p->next = n;
611 free(c);
612 c = n;
613 } else {
614 // keep
615 if (p == NULL) list = c;
616 p = c;
617 c = c->next;
620 return list;
624 // returns new head (created item)
625 static LabelRefInfo *addLabelRefToList (LabelRefInfo *list, int pc) {
626 LabelRefInfo *res;
628 res = calloc(1, sizeof(LabelRefInfo));
629 if (res == NULL) fatal("out of memory");
630 res->next = list;
631 res->pc = pc;
632 res->size = 2;
633 return res;
637 static void freeLabelRefList (LabelRefInfo *list) {
638 while (list != NULL) {
639 LabelRefInfo *r = list;
641 list = r->next;
642 free(r);
647 static void addExternRef (LabelInfo *l, int pc, int size) {
648 if (l == &labelTempBack || l == &labelTempFwd) return;
649 if (l != NULL && l->ext < 0) {
650 if (size <= 0) {
651 size = (l->type == LB_CODE || l->type == LB_CONST || l->type == LB_SVAR) ? 2 : 1;
653 l->refs = addLabelRefToList(l->refs, pc);
654 l->refs->size = size;
659 static void addLabelRef (LabelInfo *l, int pc) {
660 if (l == &labelTempBack) {
661 if (prevTmpLabelPC < 0) fatal("no backward '@@' defined");
662 //fprintf(stderr, "backref to '@@'\n");
663 vmcode[pc+0] = prevTmpLabelPC&0xff;
664 vmcode[pc+1] = (prevTmpLabelPC>>8)&0xff;
665 return;
667 if (l == &labelTempFwd) {
668 //fprintf(stderr, "fwdref to '@@'\n");
669 refFwd = addLabelRefToList(refFwd, pc);
670 return;
672 if (l != NULL) {
673 l->used = 1;
674 addExternRef(l, pc, -1);
675 if (l->type == LB_CODE && l->ext >= 0) {
676 // record fixup info for undefined code labels
677 if (l->value < 0) l->fixes = addLabelRefToList(l->fixes, pc);
678 // record reference info for code labels
679 relrefs = addLabelRefToList(relrefs, pc);
685 static void fixupFwdTmpRefs (void) {
686 for (LabelRefInfo *fix = refFwd; fix != NULL; fix = fix->next) {
687 vmcode[fix->pc+0] = pc&0xff;
688 vmcode[fix->pc+1] = (pc>>8)&0xff;
690 freeLabelRefList(refFwd);
691 refFwd = NULL;
695 static void fixupLabelRefs (LabelInfo *l, int pc) {
696 if (l == &labelTempBack || l == &labelTempFwd) return;
697 if (l != NULL) {
698 l->used = 1;
699 l->value = pc;
700 for (LabelRefInfo *fix = l->fixes; fix != NULL; fix = fix->next) {
701 vmcode[fix->pc+0] = (l->value)&0xff;
702 vmcode[fix->pc+1] = ((l->value)>>8)&0xff;
704 freeLabelRefList(l->fixes);
705 l->fixes = NULL;
710 static void checkLabels (void) {
711 if (refFwd != NULL) fatal("unresolved forward references to '@@' found");
712 for (LabelInfo *l = labels; l != NULL; l = l->next) {
713 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
714 if (!l->used) {
715 l->used = -1;
716 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);
722 static void freeLabels (void) {
723 checkLabels();
724 while (labels) {
725 LabelInfo *l = labels;
727 labels = l->next;
728 freeLabelRefList(l->fixes);
729 freeLabelRefList(l->refs);
730 free(l->name);
731 free(l);
736 static void freeLocalLabels (int onlyVars) {
737 LabelInfo *p = NULL, *l = labels;
739 if (refFwd != NULL) fatal("unresolved references to '@@' found");
740 prevTmpLabelPC = -1;
741 while (l != NULL) {
742 LabelInfo *n = l->next;
744 if ((!onlyVars || l->type != LB_CODE) && l->name[0] == '.') {
745 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
746 if (!l->used) {
747 l->used = -1;
748 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);
750 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
751 freeLabelRefList(l->fixes);
752 freeLabelRefList(l->refs);
753 free(l->name);
754 free(l);
755 if (p != NULL) p->next = n; else labels = n;
756 } else {
757 p = l;
759 l = n;
764 static LabelInfo *findLabel (const char *name) {
765 if (name && strcmp(name, "@@b") == 0) return &labelTempBack;
766 if (name && strcmp(name, "@@f") == 0) return &labelTempFwd;
767 if (name && strcmp(name, ".") == 0) {
768 //fprintf(stderr, "LOOKUP: '@@'\n");
769 return NULL;
771 if (name && name[0] == '@') ++name;
772 if (!name || !name[0]) return NULL;
773 for (LabelInfo *l = labels; l != NULL; l = l->next) if (strcmp(l->name, name) == 0) return l;
774 return NULL;
778 static LabelInfo *addLabel (const char *name) {
779 LabelInfo *l;
781 if (!name || !name[0]) fatal("internal error: empty label name");
782 if (strcmp(name, "@@b") == 0 || strcmp(name, "@@f") == 0 || strcmp(name, ".") == 0) fatal("can't define special label: '%s'", name);
783 if (findLabel(name)) fatal("duplicate label: '%s'", name);
784 l = calloc(1, sizeof(LabelInfo));
785 if (l == NULL) fatal("out of memory");
786 l->name = strdup(name);
787 if (l->name == NULL) fatal("out of memory");
788 l->type = LB_CODE;
789 l->value = -1;
790 l->ext = 0;
791 l->fixes = NULL;
792 l->refs = NULL;
793 l->next = labels;
794 labels = l;
795 return l;
799 static void newTempLabel (void) {
800 fixupFwdTmpRefs();
801 prevTmpLabelPC = pc;
802 //fprintf(stderr, "new '@@'\n");
806 ////////////////////////////////////////////////////////////////////////////////
807 typedef struct {
808 LabelInfo *l; // !=NULL: label
809 int var; // [...]
810 int value; // if l==NULL
811 int vartype; // 0: global; 1: tlocal; 2: stack
812 } OperandInfo;
815 static void setIntOperand (OperandInfo *op, int value) {
816 op->l = NULL;
817 op->var = 0;
818 op->value = value;
819 op->vartype = -1;
823 static void setLabelOperand (OperandInfo *op, LabelInfo *l) {
824 op->l = l;
825 op->var = 0;
826 op->value = 0;
827 op->vartype = -1;
831 static void getOperand (OperandInfo *op, int wantCode) {
832 if (token == TK_ID) {
833 LabelInfo *l = findLabel(tstr);
835 if (l == NULL) {
836 // new code label
837 l = addLabel(tstr);
838 l->type = LB_CODE;
839 l->value = -1;
840 } else {
841 if (wantCode && l->type != LB_CODE) fatal("code offset expected");
843 setLabelOperand(op, l);
844 nextToken();
845 return;
848 if (token == TK_NUM) {
849 if (wantCode) fatal("code offset expected");
850 setIntOperand(op, tint);
851 nextToken();
852 return;
855 if (token == '[') {
856 //if (wantCode) fatal("code offset expected");
857 nextToken();
858 if (token == TK_ID) {
859 LabelInfo *l = findLabel(tstr);
861 if (l == NULL || l->type == LB_CODE) fatal("unknown variable: '%s'", tstr);
862 setLabelOperand(op, l);
863 } else if (token == '@' || token == '.') {
864 int loc = token;
866 if (nextToken() != TK_NUM) fatal("index expected");
867 setIntOperand(op, tint);
868 if (loc != '.') {
869 // not stack
870 if (tint < 0 || tint > 126) fatal("invalid variable index");
871 op->vartype = loc=='@' ? 0 : 1;
872 } else {
873 // stack
874 op->vartype = 2;
876 } else if (token == TK_NUM) {
877 // local
878 if (tint < 0 || tint > 126) fatal("invalid variable index");
879 setIntOperand(op, tint);
880 op->vartype = 1;
881 } else {
882 fatal("invalid operand");
884 op->var = 1;
885 if (nextToken() != ']') fatal("']' expected");
886 nextToken();
887 return;
890 fprintf(stderr, "*%d [%s] %d\n", token, tstr, tint);
891 fatal("invalid operand");
895 static inline int hasOperand (void) {
896 return (token != TK_EOF && token != TK_LABELDEF && token < TK_VM_OPERATOR);
900 ////////////////////////////////////////////////////////////////////////////////
901 static void emitByte (int b) {
902 if (b < 0 || b > 255) fatal("internal error");
903 if (pc >= 32768) fatal("code too big");
904 vmcode[pc++] = b;
908 static void emitOpCode (int opc, int opcount) {
909 if (opc < 0 || opc > 63 || opcount < 0 || opcount > 3) fatal("internal error");
910 emitByte(opc|(opcount<<6));
915 static void emitInteger (int val) {
916 emitByte(255); // special
917 emitByte(val&0xff);
918 emitByte((val>>8)&0xff);
923 static void emitOperand (OperandInfo *op) {
924 if (!op || op->var < 0) return;
926 if (op->var > 0) {
927 // variable
928 if (op->l) {
929 // from label
930 op->l->used = 1;
931 switch (op->l->type) {
932 case LB_GVAR:
933 emitByte(op->l->value|0x80);
934 addLabelRef(op->l, pc-1);
935 if (op->l->ext >= 0 && op->l->name[0] != '.') addGVarFixup(pc-1);
936 break;
937 case LB_TVAR:
938 emitByte(op->l->value);
939 addLabelRef(op->l, pc-1);
940 if (op->l->ext >= 0 && op->l->name[0] != '.') addTVarFixup(pc-1);
941 break;
942 case LB_SVAR:
943 emitByte(127); // special
944 emitByte(op->l->value&0xff);
945 emitByte((op->l->value>>8)&0xff);
946 addLabelRef(op->l, pc-2);
947 break;
948 default: fatal("internal error");
950 } else {
951 // from value
952 switch (op->vartype) {
953 case 0: emitByte(op->value|0x80); break; // global
954 case 1: emitByte(op->value); break; // tlocal
955 case 2: // stack
956 emitByte(127); // special
957 emitByte(op->value&0xff);
958 emitByte((op->value>>8)&0xff);
959 break;
960 default: fatal("internal error");
963 return;
966 if (op->var == 0) {
967 // immediate
968 emitByte(255); // special
969 if (op->l) {
970 // from label
971 op->l->used = 1;
972 emitByte(op->l->value&0xff);
973 emitByte((op->l->value>>8)&0xff);
974 addLabelRef(op->l, pc-2);
975 } else {
976 // direct
977 if (op->value < -32767 || op->value > 32767) fatal("invalid value");
978 emitByte(op->value&0xff);
979 emitByte((op->value>>8)&0xff);
985 static void emitInstruction (int opc, OperandInfo *op0, OperandInfo *op1, OperandInfo *op2) {
986 int ocnt = 0;
988 if (op0 && op0->var >= 0) ++ocnt;
989 if (ocnt == 1 && op1 && op1->var >= 0) ++ocnt;
990 if (ocnt == 2 && op2 && op2->var >= 0) ++ocnt;
991 emitOpCode(opc, ocnt);
992 if (ocnt > 0) emitOperand(op0);
993 if (ocnt > 1) emitOperand(op1);
994 if (ocnt > 2) emitOperand(op2);
998 ////////////////////////////////////////////////////////////////////////////////
999 static void doNoOperands (int opcode) {
1000 emitInstruction(opcode, NULL, NULL, NULL);
1004 static void doMath (int opcode) {
1005 OperandInfo op0, op1, op2;
1007 op0.var = op1.var = op2.var = -1;
1008 if (hasOperand()) {
1009 getOperand(&op0, 0);
1010 if (token == ',') {
1011 nextToken();
1012 getOperand(&op1, 0);
1013 if (token == ',') {
1014 nextToken();
1015 getOperand(&op2, 0);
1016 if (op2.var != 1) fatal("variable expected as third operand");
1017 } else {
1018 if (op0.var != 1) fatal("variable expected as first operand");
1022 emitInstruction(opcode, &op0, &op1, &op2);
1026 static void doJXX (int opcode) {
1027 OperandInfo op0, op1, op2;
1029 op0.var = op1.var = op2.var = -1;
1030 if (hasOperand()) {
1031 getOperand(&op0, 1);
1032 if (token == ',') {
1033 nextToken();
1034 getOperand(&op1, 0);
1035 if (token == ',') {
1036 nextToken();
1037 getOperand(&op2, 0);
1041 emitInstruction(opcode, &op0, &op1, &op2);
1045 static void doBranch (int opcode) {
1046 OperandInfo op0;
1048 op0.var = -1;
1049 if (hasOperand()) {
1050 getOperand(&op0, 1);
1052 emitInstruction(opcode, &op0, NULL, NULL);
1056 static void doBSR (int opcode) {
1057 OperandInfo op0, op1, op2;
1059 op0.var = op1.var = op2.var = -1;
1060 if (hasOperand()) {
1061 getOperand(&op0, 1);
1062 if (token == ',') {
1063 nextToken();
1064 getOperand(&op1, 0);
1065 if (token == ',') {
1066 nextToken();
1067 getOperand(&op2, 0);
1071 emitInstruction(opcode, &op0, &op1, &op2);
1075 static void doMap (int opcode, int lastMBV) {
1076 OperandInfo op0, op1, op2;
1078 op0.var = op1.var = op2.var = -1;
1079 if (hasOperand()) {
1080 getOperand(&op0, 0);
1081 if (token == ',') {
1082 nextToken();
1083 getOperand(&op1, 0);
1084 if (token == ',') {
1085 nextToken();
1086 getOperand(&op2, 0);
1090 emitInstruction(opcode, &op0, &op1, &op2);
1094 static void doRST (int opcode) {
1095 OperandInfo op0, op1, op2;
1097 op0.var = op1.var = op2.var = -1;
1098 if (hasOperand()) {
1099 getOperand(&op0, 0);
1100 if (token == ',') {
1101 nextToken();
1102 getOperand(&op1, 0);
1103 if (token == ',') {
1104 nextToken();
1105 getOperand(&op2, 0);
1109 emitInstruction(opcode, &op0, &op1, &op2);
1113 static void doSet (int opcode) {
1114 OperandInfo op0, op1, op2;
1116 op0.var = op1.var = op2.var = -1;
1117 getOperand(&op0, 0);
1118 if (token == ',') {
1119 nextToken();
1120 getOperand(&op1, 0);
1121 if (token == ',') {
1122 nextToken();
1123 getOperand(&op2, 0);
1126 if (op2.var < 0 && op0.var != 1) fatal("first operand should be var");
1127 emitInstruction(opcode, &op0, &op1, &op2);
1131 static void doDrop (int opcode) {
1132 OperandInfo op0;
1134 op0.var = -1;
1135 if (hasOperand()) getOperand(&op0, 0);
1136 if (op0.var > 0) fatal("number expected");
1137 if (op0.value < 0) fatal("positive number expected");
1138 emitInstruction(opcode, &op0, NULL, NULL);
1142 static void doPush (int opcode) {
1143 OperandInfo op0, op1, op2;
1145 op0.var = op1.var = op2.var = -1;
1146 getOperand(&op0, 0);
1147 if (token == ',') {
1148 nextToken();
1149 getOperand(&op1, 0);
1150 if (token == ',') {
1151 nextToken();
1152 getOperand(&op2, 0);
1155 emitInstruction(opcode, &op0, &op1, &op2);
1159 static void doPop (int opcode) {
1160 OperandInfo op0, op1, op2;
1162 op0.var = op1.var = op2.var = -1;
1163 if (hasOperand()) {
1164 getOperand(&op0, 0);
1165 if (token == ',') {
1166 nextToken();
1167 getOperand(&op1, 0);
1168 if (token == ',') {
1169 nextToken();
1170 getOperand(&op2, 0);
1174 emitInstruction(opcode, &op0, &op1, &op2);
1178 static void doSwap (int opcode) {
1179 OperandInfo op0, op1;
1181 op0.var = op1.var = -1;
1182 if (hasOperand()) {
1183 getOperand(&op0, 0);
1184 if (token == ',') {
1185 nextToken();
1186 getOperand(&op1, 0);
1189 emitInstruction(opcode, &op0, &op1, NULL);
1193 static void doDepth (int opcode) {
1194 OperandInfo op0;
1196 op0.var = -1;
1197 if (hasOperand()) getOperand(&op0, 0);
1198 emitInstruction(opcode, &op0, NULL, NULL);
1202 static void doPickRoll (int opcode) {
1203 OperandInfo op0, op1;
1205 op0.var = op1.var = -1;
1206 if (hasOperand()) {
1207 getOperand(&op0, 0);
1208 if (token == ',') {
1209 nextToken();
1210 getOperand(&op1, 0);
1211 if (op1.var != 1) fatal("second argument must be variable");
1214 emitInstruction(opcode, &op0, &op1, NULL);
1218 static void doNew (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 doTId (int opcode) {
1234 OperandInfo op0;
1236 op0.var = -1;
1237 if (hasOperand()) getOperand(&op0, 0);
1238 emitInstruction(opcode, &op0, NULL, NULL);
1242 static void doKillSusRes (int opcode) {
1243 OperandInfo op0;
1245 op0.var = -1;
1246 if (hasOperand()) getOperand(&op0, 0);
1247 emitInstruction(opcode, &op0, NULL, NULL);
1251 static void doSta (int opcode) {
1252 OperandInfo op0, op1;
1254 op0.var = op1.var = -1;
1255 if (hasOperand()) {
1256 getOperand(&op0, 0);
1257 if (token == ',') {
1258 nextToken();
1259 getOperand(&op1, 0);
1260 if (op1.var != 1) fatal("variable expected");
1263 emitInstruction(opcode, &op0, &op1, NULL);
1267 static void doGet (int opcode) {
1268 OperandInfo op0, op1, op2;
1270 op0.var = op1.var = op2.var = -1;
1271 getOperand(&op0, 0);
1272 if (token != ',') fatal("at least two operands expected");
1273 nextToken();
1274 getOperand(&op1, 0);
1275 if (token == ',') {
1276 nextToken();
1277 getOperand(&op2, 0);
1278 if (op2.var != 1) fatal("variable expected as third operand");
1280 emitInstruction(opcode, &op0, &op1, &op2);
1284 static void doRXC (int opcode) {
1285 OperandInfo op0, op1;
1287 op0.var = op1.var = -1;
1288 if (hasOperand()) {
1289 getOperand(&op0, 0);
1290 if (token == ',') {
1291 nextToken();
1292 getOperand(&op1, 0);
1293 if (op1.var != 1) fatal("variable expected as second operand");
1296 emitInstruction(opcode, &op0, &op1, NULL);
1300 static void doWXC (int opcode) {
1301 OperandInfo op0, op1;
1303 op0.var = op1.var = -1;
1304 if (hasOperand()) {
1305 getOperand(&op0, 0);
1306 if (token == ',') {
1307 nextToken();
1308 getOperand(&op1, 0);
1311 emitInstruction(opcode, &op0, &op1, NULL);
1315 static void doRet (int opcode) {
1316 OperandInfo op0, op1, op2;
1318 op0.var = op1.var = op2.var = -1;
1319 if (hasOperand()) {
1320 getOperand(&op0, 0);
1321 if (token != ',') fatal("at least two operands expected");
1322 nextToken();
1323 getOperand(&op1, 0);
1324 if (token == ',') {
1325 nextToken();
1326 getOperand(&op2, 0);
1329 emitInstruction(opcode, &op0, &op1, &op2);
1333 ////////////////////////////////////////////////////////////////////////////////
1334 static void parseInlcude (void) {
1335 static char fname[8192], *t;
1337 tokenWantFileName = 1;
1338 if (nextToken() != TK_ID) fatal("identifier expected");
1339 tokenWantFileName = 0;
1340 strcpy(fname, ictx->fname);
1341 #ifdef _WIN32
1342 for (t = fname; *t; ++t) if (*t == '\\') *t = '/';
1343 #endif
1344 t = strrchr(fname, '/');
1345 if (t != NULL) t[1] = 0; else fname[0] = 0;
1346 strcat(fname, tstr);
1347 if (incLevel > 64) fatal("too many includes");
1348 openFile(fname);
1349 nextToken();
1353 static void parseVarList (int type, int local, int gotext) {
1354 for (;;) {
1355 LabelInfo *l;
1357 if (nextToken() != TK_ID) fatal("identifier expected");
1358 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1359 if (tstr[0] == '.' && local <= 0) fatal("invalid variable name: '%s'", tstr);
1360 if (tstr[0] != '.' && local > 0) fatal("invalid variable name: '%s'", tstr);
1361 l = findLabel(tstr);
1362 if (l != NULL) {
1363 if (gotext) {
1364 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1365 if (l->type != type) fatal("can't change existing extern label type: '%s'", tstr);
1366 } else {
1367 fatal("duplicate variable or label: '%s'", tstr);
1370 if (l == NULL) {
1371 l = addLabel(tstr);
1372 l->type = type;
1373 if (local > 0 && vtloc <= vtlast) fatal("too many local vars");
1374 if (gotext) {
1375 l->value = 0;
1376 } else {
1377 l->value = (local>0)?(--vtloc):(type==LB_GVAR?vglast++:vtlast++);
1379 if (local < 0) l->ext = 1;
1381 if (gotext) l->ext = -1;
1382 if (l->value > 126) fatal("too many vars");
1383 if (nextToken() != ',') break;
1388 static void parseUsedVarList (void) {
1389 for (;;) {
1390 LabelInfo *l;
1392 if (nextToken() != TK_ID) fatal("identifier expected");
1393 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1394 l = findLabel(tstr);
1395 if (l == NULL) fatal("unknown label: '%s'", tstr);
1396 l->used = 1;
1397 if (nextToken() != ',') break;
1402 static void parseLocList (void) {
1403 for (;;) {
1404 LabelInfo *l;
1406 if (nextToken() != TK_ID) fatal("identifier expected");
1407 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1408 if (tstr[0] != '.') fatal("local identifier expected instead of '%s'", tstr);
1409 l = findLabel(tstr);
1410 if (l != NULL) fatal("can't redefine label as local: '%s'", tstr);
1411 l = addLabel(tstr);
1412 l->ext = 0;
1413 l->type = LB_SVAR;
1414 l->value = -1;
1415 if (nextToken() != '=') fatal("'=' expected");
1416 if (nextToken() != TK_NUM) fatal("number expected");
1417 l->value = tint;
1418 if (nextToken() != ',') break;
1423 static void parseConst (int ext, int gotext) {
1424 LabelInfo *l;
1426 if (nextToken() != TK_ID) fatal("identifier expected");
1427 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1428 if (tstr[0] == '.') fatal("invalid constant name: '%s'", tstr);
1429 l = findLabel(tstr);
1430 if (l != NULL) {
1431 if (gotext) {
1432 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1433 if (l->type != LB_CONST) fatal("can't change existing extern label type: '%s'", tstr);
1434 } else {
1435 fatal("constant must be unique: '%s'", tstr);
1438 if (l == NULL) {
1439 l = addLabel(tstr);
1440 l->type = LB_CONST;
1441 l->value = 0;
1443 if (!gotext) {
1444 if (nextToken() == '=') nextToken();
1445 if (token != TK_NUM) fatal("constant must be numeric");
1446 l->value = tint;
1448 if (ext) l->ext = 1;
1449 if (gotext) l->ext = -1;
1450 nextToken();
1454 static char procname[8192];
1455 static int proclocals = 0;
1456 static int procargs = 0;
1457 static int lastWasReturn = 0;
1460 static void parseProc (int ext) {
1461 LabelInfo *l, *a = NULL;
1462 int spt;
1464 lastWasReturn = 0;
1465 if (nextToken() != TK_ID) fatal("identifier expected");
1466 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1467 if (tstr[0] == '.') fatal("invalid proc name: '%s'", tstr);
1468 if (procname[0]) fatal("unclosed proc: '%s'", procname);
1469 strcpy(procname, tstr);
1470 proclocals = 0;
1471 procargs = 0;
1472 freeLocalLabels(0); // vars and code
1474 l = findLabel(tstr);
1475 if (l != NULL) {
1476 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate proc label: '%s'", tstr);
1477 fixupLabelRefs(l, pc);
1478 } else {
1479 l = addLabel(tstr);
1480 l->type = LB_CODE;
1481 l->value = pc;
1483 if (ext) l->ext = 1;
1485 nextToken();
1487 while (token == TK_LABELDEF && (strcmp(tstr, "arg") == 0 || strcmp(tstr, "args") == 0)) {
1488 for (;;) {
1489 if (nextToken() != TK_ID) fatal("identifier expected");
1490 if (tstr[0] != '.') fatal("argument name must starts with '.'");
1491 l = findLabel(tstr);
1492 if (l != NULL) fatal("duplicate argument: '%s'", l->name);
1493 l = addLabel(tstr);
1494 l->type = LB_SVAR;
1495 l->value = 666;
1496 ++procargs;
1497 if (nextToken() != ',') break;
1499 a = labels; //HACK!
1500 // fix values
1501 // -1: return address, -2: last arg
1502 for (spt = -2, l = a; l->type == LB_SVAR; l = l->next) {
1503 l->value = spt;
1504 --spt;
1508 spt = -1; // first local
1509 while (token == TK_LABELDEF && (strcmp(tstr, "local") == 0 || strcmp(tstr, "locals") == 0)) {
1510 for (;;) {
1511 if (nextToken() != TK_ID) fatal("identifier expected");
1512 if (tstr[0] != '.') fatal("local variable name must starts with '.'");
1513 l = findLabel(tstr);
1514 if (l != NULL) fatal("duplicate local: '%s'", l->name);
1515 l = addLabel(tstr);
1516 l->type = LB_SVAR;
1517 l->value = spt--;
1518 ++proclocals;
1519 // fix args
1520 for (l = a; l != NULL && l->type == LB_SVAR; l = l->next) --(l->value);
1521 if (nextToken() != ',') break;
1525 if (proclocals > 0) {
1526 // allocate stack
1527 OperandInfo op0;
1529 setIntOperand(&op0, -proclocals);
1530 emitInstruction(VM_POP, &op0, NULL, NULL);
1535 static void parseEndP (void) {
1536 if (!procname[0]) fatal("'endp' without 'proc'");
1537 if (nextToken() != TK_ID) fatal("identifier expected");
1538 if (strcmp(procname, tstr) != 0) fatal("endp for '%s' in proc '%s'", tstr, procname);
1539 //if (!lastWasReturn) fatal("no 'return' in proc");
1540 nextToken();
1541 procname[0] = 0;
1542 freeLocalLabels(0);
1546 static void doReturn (void) {
1547 OperandInfo op0, op1, op2;
1549 if (!procname[0]) fatal("'return' without 'proc'");
1550 lastWasReturn = 1;
1551 op0.var = op1.var = op2.var = -1;
1552 if (hasOperand()) getOperand(&op2, 0); // result
1553 setIntOperand(&op0, proclocals);
1554 setIntOperand(&op1, procargs);
1555 emitInstruction(VM_RET, &op0, &op1, &op2);
1559 static void parseLabel (int gotext) {
1560 LabelInfo *l;
1562 if (gotext && tstr[0] == '.') fatal("can't declare local extern label: '%s'", tstr);
1563 if (strcmp(tstr, ".") == 0) {
1564 newTempLabel();
1565 nextToken();
1566 return;
1568 if (tstr[0] != '.' && tstr[0] != '@') {
1569 if (!gotext) {
1570 if (procname[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname);
1572 freeLocalLabels(0); // vars and code
1574 if (tstr[0] == '@') {
1575 char *d = tstr;
1577 while (*d++) d[-1] = d[0];
1578 d[-1] = 0;
1580 l = findLabel(tstr);
1581 if (l != NULL) {
1582 if (gotext) {
1583 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1584 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
1586 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate label: '%s'", tstr);
1587 fixupLabelRefs(l, pc);
1588 } else {
1589 l = addLabel(tstr);
1590 l->type = LB_CODE;
1591 l->value = pc;
1593 if (gotext) l->ext = -1;
1594 nextToken();
1598 static void parseDW (void) {
1599 for (;;) {
1600 LabelInfo *l = NULL;
1601 nextToken();
1602 if (token == TK_ID) {
1603 l = findLabel(tstr);
1605 if (l == NULL) {
1606 l = addLabel(tstr);
1607 l->type = LB_CODE;
1608 l->value = -1;
1609 //fatal("unknown label: '%s'", tstr);
1611 tint = l->value;
1612 } else if (token != TK_NUM) {
1613 fatal("number expected");
1615 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1616 emitByte(tint&0xff);
1617 emitByte((tint>>8)&0xff);
1618 if (l != NULL) {
1619 addLabelRef(l, pc-2);
1620 addExternRef(l, pc-2, 2);
1621 if (l->ext >= 0 && l->name[0] != '.') {
1622 switch (l->type) {
1623 case LB_GVAR: addGVarFixup(pc-2); break;
1624 case LB_TVAR: addTVarFixup(pc-2); break;
1628 if (nextToken() != ',') break;
1633 // terminator eaten
1634 static void parseAndPutString (int qch) {
1635 for (;;) {
1636 int ch = nextChar();
1638 //printf("[%c] [%c]\n", ch, qch);
1639 if (ch == EOF) fatal("unterminated string");
1640 if (qch == '"') {
1641 if (ch == qch) break;
1642 if (ch == '\\') {
1643 int n;
1645 ch = nextChar();
1646 if (ch == EOF) fatal("invalid escape");
1647 switch (ch) {
1648 case 'a': emitByte('\a'); break;
1649 case 'b': emitByte('\b'); break;
1650 case 'e': emitByte('\x1b'); break;
1651 case 'f': emitByte('\f'); break;
1652 case 'n': emitByte('\n'); break;
1653 case 'r': emitByte('\r'); break;
1654 case 't': emitByte('\t'); break;
1655 case 'v': emitByte('\v'); break;
1656 case '"': case '\'': case '\\': case ' ': emitByte(ch); break;
1657 case 'x':
1658 n = digit(nextChar(), 16);
1659 if (n < 0) fatal("invalid hex escape");
1660 ch = nextChar();
1661 if (ch == EOF) fatal("invalid hex escape");
1662 if (digit(ch, 16) >= 0) {
1663 n = n*16+digit(ch, 16);
1664 } else {
1665 ungetChar(ch);
1667 emitByte(n);
1668 break;
1669 default: fatal("invalid escape: '%c'", ch);
1671 } else {
1672 emitByte(ch);
1674 } else {
1675 if (ch == qch) {
1676 ch = nextChar();
1677 if (ch == EOF) return;
1678 if (ch == qch) {
1679 emitByte(ch);
1680 continue;
1682 ungetChar(ch);
1683 break;
1684 } else {
1685 emitByte(ch);
1692 static void parseDAscii (int zeroend) {
1693 for (;;) {
1694 LabelInfo *l;
1696 nextToken();
1697 if (token == TK_EOF) break;
1698 switch (token) {
1699 case '"': tokenWantFileName = 1; parseAndPutString(token); tokenWantFileName = 0; break;
1700 case '\'': tokenWantFileName = 1; parseAndPutString(token); tokenWantFileName = 0; break;
1701 case TK_ID:
1702 l = findLabel(tstr);
1704 if (l == NULL) fatal("unknown label: '%s'", tstr);
1705 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1706 l->used = 1;
1707 addExternRef(l, pc, 1);
1708 if (l->ext >= 0 && l->name[0] != '.') {
1709 switch (l->type) {
1710 case LB_GVAR: addGVarFixup(pc); break;
1711 case LB_TVAR: addTVarFixup(pc); break;
1714 tint = l->value;
1715 // fallthru
1716 case TK_NUM:
1717 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1718 emitByte(tint&0xff);
1719 break;
1720 default:
1721 fatal("number expected");
1723 if (nextToken() != ',') break;
1725 if (zeroend) emitByte(0);
1729 static void parsePublics (void) {
1730 for (;;) {
1731 LabelInfo *l;
1733 nextToken();
1734 if (token == TK_LABELDEF) {
1735 parseLabel(0);
1736 break;
1738 if (token != TK_ID) fatal("identifier expected");
1739 if (tstr[0] == '.') fatal("invalid label name: '%s'", tstr);
1740 l = findLabel(tstr);
1741 if (l != NULL) {
1742 l->ext = 1;
1743 } else {
1744 l = addLabel(tstr);
1745 l->ext = 1;
1746 l->type = LB_CODE;
1747 l->value = -1;
1749 if (nextToken() != ',') break;
1754 static void parseMacroDef (void) {
1755 MacroDef *mc;
1756 int c;
1757 char *text = NULL;
1758 int tpos = 0, tsize = 0;
1760 void addChar (int c) {
1761 if (tpos+1 > tsize) {
1762 int newsz = tsize+1024;
1763 char *nn = realloc(text, newsz);
1765 if (nn == NULL) fatal("out of memory");
1766 text = nn;
1767 tsize = newsz;
1769 text[tpos++] = c;
1772 if (nextToken() != TK_ID) fatal("macro name expected");
1773 if (tstr[0] == '.') fatal("macros can't be local: '%s'", tstr);
1774 for (mc = macros; mc != NULL; mc = mc->next) if (strcmp(mc->name, tstr) == 0) fatal("macros can't be redefined: '%s'", tstr);
1775 if ((mc = calloc(1, sizeof(MacroDef))) == NULL) fatal("out of memory");
1776 mc->next = macros;
1777 if ((mc->name = strdup(tstr)) == NULL) fatal("out of memory");
1778 macros = mc;
1779 // now parse macro args
1780 c = nextChar();
1781 ungetChar(c);
1782 skipSpaces(0); // no newlines allowed here
1783 c = nextChar();
1784 if (c != '\n') {
1785 // has args
1786 ungetChar(c);
1787 for (;;) {
1788 if (nextToken() != TK_ID) fatal("invalid macro argument definition");
1789 if (tstr[0] == '.') fatal("macro argument name must not start with dot: '%s'", tstr);
1790 addMacroArgDef(mc, tstr);
1791 skipSpaces(1);
1792 c = nextChar();
1793 if (c != ',') { ungetChar(c); break; }
1796 // now parse macro text
1797 for (;;) {
1798 int c1;
1800 c = nextChar();
1801 switch (c) {
1802 case EOF: fatal("incomplete macro definition: '%s'", mc->name);
1803 case '\n': // check for macro end
1804 addChar(c);
1805 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1806 if (c != 'e') { ungetChar(c); break; }
1807 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1808 if (c != 'n') { ungetChar(c); addChar('e'); break; }
1809 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1810 if (c != 'd') { ungetChar(c); addChar('e'); addChar('n'); break; }
1811 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1812 if (c != 'm') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); break; }
1813 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1814 if (c != 'a') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); break; }
1815 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1816 if (c != 'c') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); break; }
1817 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1818 if (c != 'r') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); break; }
1819 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1820 if (c != 'o') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); break; }
1821 c = nextChar();
1822 if (c == EOF) goto macro_complete;
1823 if (!isspace(c) && c != ';' && c != '/') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); addChar('o'); break; }
1824 else {
1825 skipSpaces(0); // no EOLs
1826 c = nextChar();
1827 if (c != '\n') fatal("invalid endmacro: '%s' (%d)", mc->name, c);
1828 goto macro_complete;
1830 break;
1831 case '"': // string
1832 addChar(c);
1833 tokenWantFileName = 1;
1834 for (;;) {
1835 c = nextChar();
1836 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1837 addChar(c);
1838 if (c == '"') break;
1839 if (c == '\\') {
1840 c = nextChar();
1841 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1842 addChar(c);
1845 tokenWantFileName = 0;
1846 break;
1847 case '\'': // string
1848 addChar(c);
1849 tokenWantFileName = 1;
1850 for (;;) {
1851 c = nextChar();
1852 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1853 addChar(c);
1854 if (c == '\'') {
1855 c = nextChar();
1856 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1857 if (c != '\'') { ungetChar(c); break; }
1858 addChar(c);
1861 tokenWantFileName = 0;
1862 break;
1863 case ';': // comment
1864 one_line_comment:
1865 addChar('\n');
1866 for (;;) {
1867 c = nextChar();
1868 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1869 if (c == '\n') break;
1871 break;
1872 case '/':
1873 c1 = nextChar();
1874 if (c1 == '/') goto one_line_comment; // comment
1875 if (c1 == '*') {
1876 // multiline comment
1877 for (;;) {
1878 c = nextChar();
1879 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1880 if (c == '*') {
1881 c = nextChar();
1882 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1883 if (c == '/') break;
1886 } else if (c1 == EOF) {
1887 fatal("incomplete macro definition: '%s'", mc->name);
1888 } else {
1889 addChar(c);
1890 addChar(c1);
1892 break;
1893 default:
1894 addChar(c);
1895 break;
1898 macro_complete:
1899 addChar('\0');
1900 mc->text = text;
1901 nextToken();
1905 static void parseMacroInvocation (MacroDef *mc) {
1906 MStrListItem *args = NULL;
1908 if (mc->argnames != NULL) {
1909 nextToken(); // skip macro name
1910 // collect macro arguments
1911 for (MStrListItem *a = mc->argnames; a != NULL; a = a->next) {
1912 int pos, c;
1914 switch (token) {
1915 case TK_EOF: fatal("macro argument expected: '%s'", mc->name);
1916 case TK_LABELDEF: fatal("macro argument can't be label: '%s'", mc->name);
1917 case TK_NUM: sprintf(tstr, "%d", tint); break;
1918 case TK_ID: break; // ok
1919 case '"': // string
1920 pos = 0;
1921 tstr[pos++] = token;
1922 tokenWantFileName = 1;
1923 for (;;) {
1924 c = nextChar();
1925 if (c == EOF) fatal("incomplete macro argument: '%s'", mc->name);
1926 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1927 tstr[pos++] = c;
1928 if (c == '"') break;
1929 if (c == '\\') {
1930 c = nextChar();
1931 if (c == EOF) fatal("incomplete macro argument: '%s'", mc->name);
1932 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1933 tstr[pos++] = c;
1936 tokenWantFileName = 0;
1937 tstr[pos] = 0;
1938 break;
1939 case '\'': // string
1940 pos = 0;
1941 tstr[pos++] = token;
1942 tokenWantFileName = 1;
1943 for (;;) {
1944 c = nextChar();
1945 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1946 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1947 tstr[pos++] = c;
1948 if (c == '\'') {
1949 c = nextChar();
1950 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1951 if (c != '\'') { ungetChar(c); break; }
1952 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1953 tstr[pos++] = c;
1956 tokenWantFileName = 0;
1957 tstr[pos] = 0;
1958 break;
1959 default:
1960 if (token >= TK_VM_OPERATOR) break;
1961 pos = 0;
1962 tstr[pos++] = token;
1963 for (;;) {
1964 c = nextChar();
1965 if (c == EOF) break;//fatal("incomplete macro definition: '%s'", mc->name);
1966 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1967 //TODO: better comment parsing
1968 if (c == ',' || c == ';' || c == '/' || c == '\n') { ungetChar(c); break; }
1969 tstr[pos++] = c;
1971 while (isspace(tstr[pos-1])) --pos;
1972 tstr[pos] = 0;
1973 break;
1974 //fatal("invalid macro argument: '%s'", mc->name);
1976 //fprintf(stderr, "arg [%s]: [%s]\n", mc->argnames->macname, tstr);
1977 args = addMacroArg(args, mc->argnames->macname, tstr);
1978 if (a->next != NULL) {
1979 if (nextToken() != ',') fatal("macro argument expected: '%s'", mc->name);
1983 // arguments collected
1984 openMacro(mc, args);
1985 nextToken();
1989 static void process (void) {
1990 int gotext = 0;
1991 LabelInfo *l = addLabel("retval");
1993 l->type = LB_TVAR;
1994 l->value = VM_VARS_SIZE-1;
1996 memset(&labelTempBack, 0, sizeof(LabelInfo));
1997 memset(&labelTempFwd, 0, sizeof(labelTempFwd));
1999 labelTempBack.type = labelTempFwd.type = LB_CODE;
2000 labelTempBack.value = labelTempFwd.value = -1;
2002 procname[0] = 0;
2003 lastWasReturn = 0;
2004 nextToken();
2005 while (token != TK_EOF) {
2006 int opc;
2008 if (token == TK_LABELDEF) {
2009 if (strcmp(tstr, "extern") == 0) {
2010 if (gotext) fatal("double 'extern'");
2011 gotext = 1;
2012 nextToken();
2013 continue;
2015 // new label or operator
2016 if (strcmp(tstr, "include") == 0) {
2017 parseInlcude();
2018 continue;
2019 } else if (strcmp(tstr, "defloc") == 0) {
2020 if (gotext) fatal("extern labels must not be locals");
2021 parseLocList();
2022 } else if (strcmp(tstr, "defgvar") == 0) {
2023 parseVarList(LB_GVAR, 0, gotext);
2024 } else if (strcmp(tstr, "defevar") == 0) { // extern global
2025 parseVarList(LB_GVAR, -1, gotext);
2026 } else if (strcmp(tstr, "deftvar") == 0) {
2027 freeLocalLabels(1); // only vars
2028 vtloc = VM_VARS_SIZE;
2029 parseVarList(LB_TVAR, 0, gotext);
2030 } else if (strcmp(tstr, "defetvar") == 0) {
2031 freeLocalLabels(1); // only vars
2032 vtloc = VM_VARS_SIZE;
2033 parseVarList(LB_TVAR, -1, gotext);
2034 } else if (strcmp(tstr, "deflvar") == 0) {
2035 if (gotext) fatal("extern labels must not be locals");
2036 parseVarList(LB_TVAR, 1, gotext);
2037 } else if (strcmp(tstr, "public") == 0) {
2038 if (gotext) fatal("invalid extern label declaration");
2039 parsePublics();
2040 } else if (strcmp(tstr, "proc") == 0 || strcmp(tstr, "eproc") == 0) {
2041 if (gotext) fatal("invalid extern label declaration");
2042 parseProc(tstr[0] == 'e');
2043 } else if (strcmp(tstr, "endp") == 0) {
2044 if (gotext) fatal("invalid extern label declaration");
2045 parseEndP();
2046 } else if (strcmp(tstr, "const") == 0 || strcmp(tstr, "econst") == 0) {
2047 parseConst(tstr[0] == 'e', gotext);
2048 } else if (strcmp(tstr, "db") == 0) {
2049 if (gotext) fatal("invalid extern label declaration");
2050 parseDAscii(0);
2051 } else if (strcmp(tstr, "dw") == 0) {
2052 if (gotext) fatal("invalid extern label declaration");
2053 parseDW();
2054 } else if (strcmp(tstr, "da") == 0) {
2055 if (gotext) fatal("invalid extern label declaration");
2056 parseDAscii(0);
2057 } else if (strcmp(tstr, "dz") == 0) {
2058 if (gotext) fatal("invalid extern label declaration");
2059 parseDAscii(1);
2060 } else if (strcmp(tstr, "macro") == 0) {
2061 if (gotext) fatal("macros can't be external");
2062 parseMacroDef();
2063 } else if (strcmp(tstr, "used") == 0) {
2064 if (gotext) fatal("'used' labels can't be external");
2065 parseUsedVarList();
2066 } else if (strcmp(tstr, "gvarbase") == 0) {
2067 if (gotext) fatal("'gvarbase' can't be external");
2068 if (nextToken() != TK_NUM) fatal("gvarbase: number expected");
2069 vglast = tint;
2070 nextToken();
2071 } else if (strcmp(tstr, "tvarbase") == 0) {
2072 if (gotext) fatal("'tvarbase' can't be external");
2073 if (nextToken() != TK_NUM) fatal("tvarbase: number expected");
2074 vtlast = tint;
2075 nextToken();
2076 } else {
2077 parseLabel(gotext);
2079 gotext = 0;
2080 continue;
2081 } else {
2082 if (gotext) {
2083 LabelInfo *l;
2085 if (token != TK_ID) fatal("label declaration expected after 'extern'");
2086 if (tstr[0] == '.') fatal("extern label can't be local: '%s'", tstr);
2087 l = findLabel(tstr);
2088 if (l != NULL) {
2089 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
2090 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
2091 } else {
2092 l = addLabel(tstr);
2093 l->type = LB_CODE;
2094 l->value = 0;
2095 l->ext = -1;
2097 nextToken();
2098 continue;
2101 // check for macro invocation
2102 if (token == TK_ID) {
2103 for (MacroDef *mc = macros; mc != NULL; mc = mc->next) {
2104 if (strcmp(mc->name, tstr) == 0) {
2105 parseMacroInvocation(mc);
2106 continue;
2111 if (token < TK_VM_OPERATOR) {
2112 //fprintf(stderr, "%d: [%s]\n", token, tstr);
2113 fatal("mnemonics expected");
2115 opc = token;
2116 if (opc < 600) opc -= TK_VM_OPERATOR;
2117 nextToken();
2118 lastWasReturn = 0;
2119 switch (opc) {
2120 case VM_ADD:
2121 case VM_SUB:
2122 case VM_MUL:
2123 case VM_DIV:
2124 case VM_MOD:
2125 case VM_BOR:
2126 case VM_XOR:
2127 case VM_AND:
2128 doMath(opc);
2129 break;
2130 case VM_JEQ:
2131 case VM_JNE:
2132 case VM_JLT:
2133 case VM_JLE:
2134 case VM_JGT:
2135 case VM_JGE:
2136 doJXX(opc);
2137 break;
2138 case VM_JMP:
2139 doBranch(opc);
2140 break;
2141 case VM_END:
2142 doNoOperands(opc);
2143 break;
2144 case VM_BSR:
2145 doBSR(opc);
2146 break;
2147 case VM_NEW:
2148 doNew(opc);
2149 break;
2150 case VM_BRK:
2151 doNoOperands(opc);
2152 break;
2153 case VM_SET:
2154 doSet(opc);
2155 break;
2156 case VM_GET:
2157 doGet(opc);
2158 break;
2159 case VM_PSH:
2160 doPush(opc);
2161 break;
2162 case VM_POP:
2163 doPop(opc);
2164 break;
2165 case VM_SWP:
2166 doSwap(opc);
2167 break;
2168 case VM_PCK:
2169 case VM_ROL:
2170 doPickRoll(opc);
2171 break;
2172 case VM_DPT:
2173 doDepth(opc);
2174 break;
2175 case VM_TID:
2176 doTId(opc);
2177 break;
2178 case VM_KIL:
2179 case VM_SUS:
2180 case VM_RES:
2181 doKillSusRes(opc);
2182 break;
2183 case VM_STA:
2184 doSta(opc);
2185 break;
2186 case VM_RXC:
2187 doRXC(opc);
2188 break;
2189 case VM_WXC:
2190 doWXC(opc);
2191 break;
2192 case VM_RST:
2193 doRST(opc);
2194 break;
2195 case VM_MGF:
2196 case VM_MGB:
2197 doMap(opc, 1);
2198 break;
2199 case VM_MSF:
2200 case VM_MSB:
2201 doMap(opc, 0);
2202 break;
2203 case VM_RET:
2204 if (procname[0]) doReturn(); else doRet(opc);
2205 break;
2206 case VMX_DRP:
2207 doDrop(VM_POP);
2208 break;
2209 case VMX_DUP:
2210 doNoOperands(VM_PSH);
2211 break;
2212 case VMX_RTN:
2213 doRet(opc);
2214 break;
2215 case VMX_RETURN:
2216 doReturn();
2217 break;
2218 default:
2219 fatal("not yet");
2222 if (procname[0]) fatal("'proc' without 'endp': '%s'", procname);
2226 ////////////////////////////////////////////////////////////////////////////////
2227 static int cfWriteByte (FILE *fl, int value) {
2228 unsigned char b;
2230 b = (value&0xff)^SECRET;
2231 if (fwrite(&b, 1, 1, fl) != 1) return -1;
2232 return 0;
2236 static int cfWriteWord (FILE *fl, int value) {
2237 if (cfWriteByte(fl, value) != 0) return -1;
2238 if (cfWriteByte(fl, value>>8) != 0) return -1;
2239 return 0;
2243 static int cfWriteCode (FILE *fl) {
2244 for (int f = 0; f < pc; ++f) if (cfWriteByte(fl, vmcode[f]) != 0) return -1;
2245 return 0;
2249 static int cfWriteRels (FILE *fl) {
2250 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) {
2251 if (cfWriteWord(fl, r->pc) != 0) return -1;
2253 return 0;
2257 static int cfWritePublicLabels (FILE *fl) {
2258 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2259 if (l->ext >= 0 && l->name[0]) {
2260 int len = strlen(l->name);
2262 if (cfWriteByte(fl, l->type|(l->ext>0?0x80:0)) != 0) return -1;
2264 switch (l->type) {
2265 case LB_GVAR:
2266 case LB_TVAR:
2267 if (cfWriteByte(fl, l->value) != 0) return -1;
2268 break;
2269 case LB_SVAR:
2270 case LB_CODE:
2271 case LB_CONST:
2272 if (cfWriteWord(fl, l->value) != 0) return -1;
2273 break;
2274 default:
2275 abort();
2278 if (len > 255) len = 255;
2279 if (cfWriteByte(fl, len) != 0) return -1;
2280 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
2283 return 0;
2287 static int cfWriteExtLabels (FILE *fl) {
2288 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2289 if (l->ext < 0 && l->name[0] && l->refs != NULL) {
2290 int len = strlen(l->name), rcnt = 0;
2292 if (cfWriteByte(fl, l->type) != 0) return -1;
2293 if (len > 255) len = 255;
2294 if (cfWriteByte(fl, len) != 0) return -1;
2295 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
2297 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) ++rcnt;
2298 if (cfWriteWord(fl, rcnt) != 0) return -1;
2299 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) {
2300 if (cfWriteByte(fl, r->size) != 0) return -1;
2301 if (cfWriteWord(fl, r->pc) != 0) return -1;
2305 return 0;
2309 static int cfWriteVarFixups (FILE *fl, const VarFixup *list) {
2310 int cnt = 0;
2312 for (const VarFixup *f = list; f != NULL; f = f->next) ++cnt;
2313 if (cfWriteWord(fl, cnt) != 0) return -1;
2314 for (const VarFixup *f = list; f != NULL; f = f->next) {
2315 if (cfWriteWord(fl, f->pc) != 0) return -1;
2317 return 0;
2321 static int writeCodeFile (FILE *fl) {
2322 static const char *sign = "AVM2";
2323 int lcnt = 0, elcnt = 0;
2324 int rcnt = 0;
2326 relrefs = lrefRemoveDups(relrefs);
2327 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) ++rcnt;
2328 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2329 if (l->name[0]) {
2330 if (l->ext >= 0) ++lcnt;
2331 if (l->ext < 0) {
2332 l->refs = lrefRemoveDups(l->refs);
2333 if (l->refs != NULL) ++elcnt;
2337 fprintf(stderr, "%d bytes of code, %d public labels, %d fixups, %d externs; maxgvar: %d, maxtvar: %d\n", pc, lcnt, rcnt, elcnt, vglast, vtlast);
2339 if (fwrite(sign, 4, 1, fl) != 1) return -1;
2340 // code size
2341 if (cfWriteWord(fl, pc) != 0) return -1;
2342 // number of fixups
2343 if (cfWriteWord(fl, rcnt) != 0) return -1;
2344 // number of extern labels
2345 if (cfWriteWord(fl, elcnt) != 0) return -1;
2346 // number of labels
2347 if (cfWriteWord(fl, lcnt) != 0) return -1;
2348 // last used global
2349 if (cfWriteWord(fl, vglast) != 0) return -1;
2350 // last used thread local
2351 if (cfWriteWord(fl, vtlast) != 0) return -1;
2353 if (cfWriteCode(fl) != 0) return -1;
2354 if (cfWriteRels(fl) != 0) return -1;
2355 if (cfWriteExtLabels(fl) != 0) return -1;
2356 if (cfWritePublicLabels(fl) != 0) return -1;
2357 if (cfWriteVarFixups(fl, gvfixes) != 0) return -1;
2358 if (cfWriteVarFixups(fl, tvfixes) != 0) return -1;
2360 return 0;
2364 ////////////////////////////////////////////////////////////////////////////////
2365 #ifdef _WIN32
2366 # include "cmdline.c"
2367 #endif
2370 ////////////////////////////////////////////////////////////////////////////////
2371 int main (int argc, char *argv[]) {
2372 #ifdef _WIN32
2373 cmdLineParse();
2374 argc = k8argc;
2375 argv = k8argv;
2376 #endif
2378 for (int f = 1; f < argc; ++f) {
2379 if (strcmp(argv[f], "-Wno") == 0) {
2380 optWarnings = 0;
2381 for (int c = f+1; c < argc; ++c) argv[c-1] = argv[c];
2382 argv[--argc] = NULL;
2383 --f;
2384 continue;
2388 if (argc != 3) {
2389 fprintf(stderr, "usage: awasm infile outfile\n");
2390 return 1;
2392 openFile(argv[1]);
2393 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
2394 process();
2395 while (ictx != NULL) closeFile();
2396 freeLocalLabels(0); // vars and code
2397 checkLabels();
2398 //if (argc > 3) dumpGlobalVars(argv[3]);
2400 FILE *fo = fopen(argv[2], "wb");
2401 int res;
2403 if (fo == NULL) { fprintf(stderr, "FATAL: can't create output file: '%s'\n", argv[2]); return 1; }
2404 res = writeCodeFile(fo);
2405 if (fclose(fo) != 0) res = -1;
2406 if (res != 0) {
2407 fprintf(stderr, "FATAL: error writing output file: '%s'\n", argv[2]);
2408 unlink(argv[2]);
2409 return 1;
2412 freeLabels();
2413 freeLabelRefList(relrefs);
2414 freeMacroList();
2415 return 0;