added pure shell build script for lazy people
[awish.git] / src / awasm.c
blob2d903e2961a30558c4adf98fc472869d6bd67e33
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 relrefs = addLabelRefToList(relrefs, pc);
666 return;
668 if (l == &labelTempFwd) {
669 //fprintf(stderr, "fwdref to '@@'\n");
670 refFwd = addLabelRefToList(refFwd, pc);
671 relrefs = addLabelRefToList(relrefs, pc);
672 return;
674 if (l != NULL) {
675 l->used = 1;
676 addExternRef(l, pc, -1);
677 if (l->type == LB_CODE && l->ext >= 0) {
678 // record fixup info for undefined code labels
679 if (l->value < 0) l->fixes = addLabelRefToList(l->fixes, pc);
680 // record reference info for code labels
681 relrefs = addLabelRefToList(relrefs, pc);
687 static void fixupFwdTmpRefs (void) {
688 for (LabelRefInfo *fix = refFwd; fix != NULL; fix = fix->next) {
689 vmcode[fix->pc+0] = pc&0xff;
690 vmcode[fix->pc+1] = (pc>>8)&0xff;
692 freeLabelRefList(refFwd);
693 refFwd = NULL;
697 static void fixupLabelRefs (LabelInfo *l, int pc) {
698 if (l == &labelTempBack || l == &labelTempFwd) return;
699 if (l != NULL) {
700 l->used = 1;
701 l->value = pc;
702 for (LabelRefInfo *fix = l->fixes; fix != NULL; fix = fix->next) {
703 vmcode[fix->pc+0] = (l->value)&0xff;
704 vmcode[fix->pc+1] = ((l->value)>>8)&0xff;
706 freeLabelRefList(l->fixes);
707 l->fixes = NULL;
712 static void checkLabels (void) {
713 if (refFwd != NULL) fatal("unresolved forward references to '@@' found");
714 for (LabelInfo *l = labels; l != NULL; l = l->next) {
715 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
716 if (!l->used) {
717 l->used = -1;
718 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);
724 static void freeLabels (void) {
725 checkLabels();
726 while (labels) {
727 LabelInfo *l = labels;
729 labels = l->next;
730 freeLabelRefList(l->fixes);
731 freeLabelRefList(l->refs);
732 free(l->name);
733 free(l);
738 static void freeLocalLabels (int onlyVars) {
739 LabelInfo *p = NULL, *l = labels;
741 if (refFwd != NULL) fatal("unresolved references to '@@' found");
742 prevTmpLabelPC = -1;
743 while (l != NULL) {
744 LabelInfo *n = l->next;
746 if ((!onlyVars || l->type != LB_CODE) && l->name[0] == '.') {
747 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
748 if (!l->used) {
749 l->used = -1;
750 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);
752 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
753 freeLabelRefList(l->fixes);
754 freeLabelRefList(l->refs);
755 free(l->name);
756 free(l);
757 if (p != NULL) p->next = n; else labels = n;
758 } else {
759 p = l;
761 l = n;
766 static LabelInfo *findLabel (const char *name) {
767 if (name && strcmp(name, "@@b") == 0) return &labelTempBack;
768 if (name && strcmp(name, "@@f") == 0) return &labelTempFwd;
769 if (name && strcmp(name, ".") == 0) {
770 //fprintf(stderr, "LOOKUP: '@@'\n");
771 return NULL;
773 if (name && name[0] == '@') ++name;
774 if (!name || !name[0]) return NULL;
775 for (LabelInfo *l = labels; l != NULL; l = l->next) if (strcmp(l->name, name) == 0) return l;
776 return NULL;
780 static LabelInfo *addLabel (const char *name) {
781 LabelInfo *l;
783 if (!name || !name[0]) fatal("internal error: empty label name");
784 if (strcmp(name, "@@b") == 0 || strcmp(name, "@@f") == 0 || strcmp(name, ".") == 0) fatal("can't define special label: '%s'", name);
785 if (findLabel(name)) fatal("duplicate label: '%s'", name);
786 l = calloc(1, sizeof(LabelInfo));
787 if (l == NULL) fatal("out of memory");
788 l->name = strdup(name);
789 if (l->name == NULL) fatal("out of memory");
790 l->type = LB_CODE;
791 l->value = -1;
792 l->ext = 0;
793 l->fixes = NULL;
794 l->refs = NULL;
795 l->next = labels;
796 labels = l;
797 return l;
801 static void newTempLabel (void) {
802 fixupFwdTmpRefs();
803 prevTmpLabelPC = pc;
804 //fprintf(stderr, "new '@@'\n");
808 ////////////////////////////////////////////////////////////////////////////////
809 typedef struct {
810 LabelInfo *l; // !=NULL: label
811 int var; // [...]
812 int value; // if l==NULL
813 int vartype; // 0: global; 1: tlocal; 2: stack
814 } OperandInfo;
817 static void setIntOperand (OperandInfo *op, int value) {
818 op->l = NULL;
819 op->var = 0;
820 op->value = value;
821 op->vartype = -1;
825 static void setLabelOperand (OperandInfo *op, LabelInfo *l) {
826 op->l = l;
827 op->var = 0;
828 op->value = 0;
829 op->vartype = -1;
833 static void getOperand (OperandInfo *op, int wantCode) {
834 if (token == TK_ID) {
835 LabelInfo *l = findLabel(tstr);
837 if (l == NULL) {
838 // new code label
839 l = addLabel(tstr);
840 l->type = LB_CODE;
841 l->value = -1;
842 } else {
843 if (wantCode && l->type != LB_CODE) fatal("code offset expected");
845 setLabelOperand(op, l);
846 nextToken();
847 return;
850 if (token == TK_NUM) {
851 if (wantCode) fatal("code offset expected");
852 setIntOperand(op, tint);
853 nextToken();
854 return;
857 if (token == '[') {
858 //if (wantCode) fatal("code offset expected");
859 nextToken();
860 if (token == TK_ID) {
861 LabelInfo *l = findLabel(tstr);
863 if (l == NULL || l->type == LB_CODE) fatal("unknown variable: '%s'", tstr);
864 setLabelOperand(op, l);
865 } else if (token == '@' || token == '.') {
866 int loc = token;
868 if (nextToken() != TK_NUM) fatal("index expected");
869 setIntOperand(op, tint);
870 if (loc != '.') {
871 // not stack
872 if (tint < 0 || tint > 126) fatal("invalid variable index");
873 op->vartype = loc=='@' ? 0 : 1;
874 } else {
875 // stack
876 op->vartype = 2;
878 } else if (token == TK_NUM) {
879 // local
880 if (tint < 0 || tint > 126) fatal("invalid variable index");
881 setIntOperand(op, tint);
882 op->vartype = 1;
883 } else {
884 fatal("invalid operand");
886 op->var = 1;
887 if (nextToken() != ']') fatal("']' expected");
888 nextToken();
889 return;
892 fprintf(stderr, "*%d [%s] %d\n", token, tstr, tint);
893 fatal("invalid operand");
897 static inline int hasOperand (void) {
898 return (token != TK_EOF && token != TK_LABELDEF && token < TK_VM_OPERATOR);
902 ////////////////////////////////////////////////////////////////////////////////
903 static void emitByte (int b) {
904 if (b < 0 || b > 255) fatal("internal error");
905 if (pc >= 32768) fatal("code too big");
906 vmcode[pc++] = b;
910 static void emitOpCode (int opc, int opcount) {
911 if (opc < 0 || opc > 63 || opcount < 0 || opcount > 3) fatal("internal error");
912 emitByte(opc|(opcount<<6));
917 static void emitInteger (int val) {
918 emitByte(255); // special
919 emitByte(val&0xff);
920 emitByte((val>>8)&0xff);
925 static void emitOperand (OperandInfo *op) {
926 if (!op || op->var < 0) return;
928 if (op->var > 0) {
929 // variable
930 if (op->l) {
931 // from label
932 op->l->used = 1;
933 switch (op->l->type) {
934 case LB_GVAR:
935 emitByte(op->l->value|0x80);
936 addLabelRef(op->l, pc-1);
937 if (op->l->ext >= 0 && op->l->name[0] != '.') addGVarFixup(pc-1);
938 break;
939 case LB_TVAR:
940 emitByte(op->l->value);
941 addLabelRef(op->l, pc-1);
942 if (op->l->ext >= 0 && op->l->name[0] != '.') addTVarFixup(pc-1);
943 break;
944 case LB_SVAR:
945 emitByte(127); // special
946 emitByte(op->l->value&0xff);
947 emitByte((op->l->value>>8)&0xff);
948 addLabelRef(op->l, pc-2);
949 break;
950 default: fatal("internal error");
952 } else {
953 // from value
954 switch (op->vartype) {
955 case 0: emitByte(op->value|0x80); break; // global
956 case 1: emitByte(op->value); break; // tlocal
957 case 2: // stack
958 emitByte(127); // special
959 emitByte(op->value&0xff);
960 emitByte((op->value>>8)&0xff);
961 break;
962 default: fatal("internal error");
965 return;
968 if (op->var == 0) {
969 // immediate
970 emitByte(255); // special
971 if (op->l) {
972 // from label
973 op->l->used = 1;
974 emitByte(op->l->value&0xff);
975 emitByte((op->l->value>>8)&0xff);
976 addLabelRef(op->l, pc-2);
977 } else {
978 // direct
979 if (op->value < -32767 || op->value > 32767) fatal("invalid value");
980 emitByte(op->value&0xff);
981 emitByte((op->value>>8)&0xff);
987 static void emitInstruction (int opc, OperandInfo *op0, OperandInfo *op1, OperandInfo *op2) {
988 int ocnt = 0;
990 if (op0 && op0->var >= 0) ++ocnt;
991 if (ocnt == 1 && op1 && op1->var >= 0) ++ocnt;
992 if (ocnt == 2 && op2 && op2->var >= 0) ++ocnt;
993 emitOpCode(opc, ocnt);
994 if (ocnt > 0) emitOperand(op0);
995 if (ocnt > 1) emitOperand(op1);
996 if (ocnt > 2) emitOperand(op2);
1000 ////////////////////////////////////////////////////////////////////////////////
1001 static void doNoOperands (int opcode) {
1002 emitInstruction(opcode, NULL, NULL, NULL);
1006 static void doMath (int opcode) {
1007 OperandInfo op0, op1, op2;
1009 op0.var = op1.var = op2.var = -1;
1010 if (hasOperand()) {
1011 getOperand(&op0, 0);
1012 if (token == ',') {
1013 nextToken();
1014 getOperand(&op1, 0);
1015 if (token == ',') {
1016 nextToken();
1017 getOperand(&op2, 0);
1018 if (op2.var != 1) fatal("variable expected as third operand");
1019 } else {
1020 if (op0.var != 1) fatal("variable expected as first operand");
1024 emitInstruction(opcode, &op0, &op1, &op2);
1028 static void doJXX (int opcode) {
1029 OperandInfo op0, op1, op2;
1031 op0.var = op1.var = op2.var = -1;
1032 if (hasOperand()) {
1033 getOperand(&op0, 1);
1034 if (token == ',') {
1035 nextToken();
1036 getOperand(&op1, 0);
1037 if (token == ',') {
1038 nextToken();
1039 getOperand(&op2, 0);
1043 emitInstruction(opcode, &op0, &op1, &op2);
1047 static void doBranch (int opcode) {
1048 OperandInfo op0;
1050 op0.var = -1;
1051 if (hasOperand()) {
1052 getOperand(&op0, 1);
1054 emitInstruction(opcode, &op0, NULL, NULL);
1058 static void doBSR (int opcode) {
1059 OperandInfo op0, op1, op2;
1061 op0.var = op1.var = op2.var = -1;
1062 if (hasOperand()) {
1063 getOperand(&op0, 1);
1064 if (token == ',') {
1065 nextToken();
1066 getOperand(&op1, 0);
1067 if (token == ',') {
1068 nextToken();
1069 getOperand(&op2, 0);
1073 emitInstruction(opcode, &op0, &op1, &op2);
1077 static void doMap (int opcode, int lastMBV) {
1078 OperandInfo op0, op1, op2;
1080 op0.var = op1.var = op2.var = -1;
1081 if (hasOperand()) {
1082 getOperand(&op0, 0);
1083 if (token == ',') {
1084 nextToken();
1085 getOperand(&op1, 0);
1086 if (token == ',') {
1087 nextToken();
1088 getOperand(&op2, 0);
1092 emitInstruction(opcode, &op0, &op1, &op2);
1096 static void doRST (int opcode) {
1097 OperandInfo op0, op1, op2;
1099 op0.var = op1.var = op2.var = -1;
1100 if (hasOperand()) {
1101 getOperand(&op0, 0);
1102 if (token == ',') {
1103 nextToken();
1104 getOperand(&op1, 0);
1105 if (token == ',') {
1106 nextToken();
1107 getOperand(&op2, 0);
1111 emitInstruction(opcode, &op0, &op1, &op2);
1115 static void doSet (int opcode) {
1116 OperandInfo op0, op1, op2;
1118 op0.var = op1.var = op2.var = -1;
1119 getOperand(&op0, 0);
1120 if (token == ',') {
1121 nextToken();
1122 getOperand(&op1, 0);
1123 if (token == ',') {
1124 nextToken();
1125 getOperand(&op2, 0);
1128 if (op2.var < 0 && op0.var != 1) fatal("first operand should be var");
1129 emitInstruction(opcode, &op0, &op1, &op2);
1133 static void doDrop (int opcode) {
1134 OperandInfo op0;
1136 op0.var = -1;
1137 if (hasOperand()) getOperand(&op0, 0);
1138 if (op0.var > 0) fatal("number expected");
1139 if (op0.value < 0) fatal("positive number expected");
1140 emitInstruction(opcode, &op0, NULL, NULL);
1144 static void doPush (int opcode) {
1145 OperandInfo op0, op1, op2;
1147 op0.var = op1.var = op2.var = -1;
1148 getOperand(&op0, 0);
1149 if (token == ',') {
1150 nextToken();
1151 getOperand(&op1, 0);
1152 if (token == ',') {
1153 nextToken();
1154 getOperand(&op2, 0);
1157 emitInstruction(opcode, &op0, &op1, &op2);
1161 static void doPop (int opcode) {
1162 OperandInfo op0, op1, op2;
1164 op0.var = op1.var = op2.var = -1;
1165 if (hasOperand()) {
1166 getOperand(&op0, 0);
1167 if (token == ',') {
1168 nextToken();
1169 getOperand(&op1, 0);
1170 if (token == ',') {
1171 nextToken();
1172 getOperand(&op2, 0);
1176 emitInstruction(opcode, &op0, &op1, &op2);
1180 static void doSwap (int opcode) {
1181 OperandInfo op0, op1;
1183 op0.var = op1.var = -1;
1184 if (hasOperand()) {
1185 getOperand(&op0, 0);
1186 if (token == ',') {
1187 nextToken();
1188 getOperand(&op1, 0);
1191 emitInstruction(opcode, &op0, &op1, NULL);
1195 static void doDepth (int opcode) {
1196 OperandInfo op0;
1198 op0.var = -1;
1199 if (hasOperand()) getOperand(&op0, 0);
1200 emitInstruction(opcode, &op0, NULL, NULL);
1204 static void doPickRoll (int opcode) {
1205 OperandInfo op0, op1;
1207 op0.var = op1.var = -1;
1208 if (hasOperand()) {
1209 getOperand(&op0, 0);
1210 if (token == ',') {
1211 nextToken();
1212 getOperand(&op1, 0);
1213 if (op1.var != 1) fatal("second argument must be variable");
1216 emitInstruction(opcode, &op0, &op1, NULL);
1220 static void doNew (int opcode) {
1221 OperandInfo op0, op1;
1223 op0.var = op1.var = -1;
1224 if (hasOperand()) {
1225 getOperand(&op0, 0);
1226 if (token == ',') {
1227 nextToken();
1228 getOperand(&op1, 0);
1231 emitInstruction(opcode, &op0, &op1, NULL);
1235 static void doTId (int opcode) {
1236 OperandInfo op0;
1238 op0.var = -1;
1239 if (hasOperand()) getOperand(&op0, 0);
1240 emitInstruction(opcode, &op0, NULL, NULL);
1244 static void doKillSusRes (int opcode) {
1245 OperandInfo op0;
1247 op0.var = -1;
1248 if (hasOperand()) getOperand(&op0, 0);
1249 emitInstruction(opcode, &op0, NULL, NULL);
1253 static void doSta (int opcode) {
1254 OperandInfo op0, op1;
1256 op0.var = op1.var = -1;
1257 if (hasOperand()) {
1258 getOperand(&op0, 0);
1259 if (token == ',') {
1260 nextToken();
1261 getOperand(&op1, 0);
1262 if (op1.var != 1) fatal("variable expected");
1265 emitInstruction(opcode, &op0, &op1, NULL);
1269 static void doGet (int opcode) {
1270 OperandInfo op0, op1, op2;
1272 op0.var = op1.var = op2.var = -1;
1273 getOperand(&op0, 0);
1274 if (token != ',') fatal("at least two operands expected");
1275 nextToken();
1276 getOperand(&op1, 0);
1277 if (token == ',') {
1278 nextToken();
1279 getOperand(&op2, 0);
1280 if (op2.var != 1) fatal("variable expected as third operand");
1282 emitInstruction(opcode, &op0, &op1, &op2);
1286 static void doRXC (int opcode) {
1287 OperandInfo op0, op1;
1289 op0.var = op1.var = -1;
1290 if (hasOperand()) {
1291 getOperand(&op0, 0);
1292 if (token == ',') {
1293 nextToken();
1294 getOperand(&op1, 0);
1295 if (op1.var != 1) fatal("variable expected as second operand");
1298 emitInstruction(opcode, &op0, &op1, NULL);
1302 static void doWXC (int opcode) {
1303 OperandInfo op0, op1;
1305 op0.var = op1.var = -1;
1306 if (hasOperand()) {
1307 getOperand(&op0, 0);
1308 if (token == ',') {
1309 nextToken();
1310 getOperand(&op1, 0);
1313 emitInstruction(opcode, &op0, &op1, NULL);
1317 static void doRet (int opcode) {
1318 OperandInfo op0, op1, op2;
1320 op0.var = op1.var = op2.var = -1;
1321 if (hasOperand()) {
1322 getOperand(&op0, 0);
1323 if (token != ',') fatal("at least two operands expected");
1324 nextToken();
1325 getOperand(&op1, 0);
1326 if (token == ',') {
1327 nextToken();
1328 getOperand(&op2, 0);
1331 emitInstruction(opcode, &op0, &op1, &op2);
1335 ////////////////////////////////////////////////////////////////////////////////
1336 static void parseInlcude (void) {
1337 static char fname[8192], *t;
1339 tokenWantFileName = 1;
1340 if (nextToken() != TK_ID) fatal("identifier expected");
1341 tokenWantFileName = 0;
1342 strcpy(fname, ictx->fname);
1343 #ifdef _WIN32
1344 for (t = fname; *t; ++t) if (*t == '\\') *t = '/';
1345 #endif
1346 t = strrchr(fname, '/');
1347 if (t != NULL) t[1] = 0; else fname[0] = 0;
1348 strcat(fname, tstr);
1349 if (incLevel > 64) fatal("too many includes");
1350 openFile(fname);
1351 nextToken();
1355 static void parseVarList (int type, int local, int gotext) {
1356 for (;;) {
1357 LabelInfo *l;
1359 if (nextToken() != TK_ID) fatal("identifier expected");
1360 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1361 if (tstr[0] == '.' && local <= 0) fatal("invalid variable name: '%s'", tstr);
1362 if (tstr[0] != '.' && local > 0) fatal("invalid variable name: '%s'", tstr);
1363 l = findLabel(tstr);
1364 if (l != NULL) {
1365 if (gotext) {
1366 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1367 if (l->type != type) fatal("can't change existing extern label type: '%s'", tstr);
1368 } else {
1369 fatal("duplicate variable or label: '%s'", tstr);
1372 if (l == NULL) {
1373 l = addLabel(tstr);
1374 l->type = type;
1375 if (local > 0 && vtloc <= vtlast) fatal("too many local vars");
1376 if (gotext) {
1377 l->value = 0;
1378 } else {
1379 l->value = (local>0)?(--vtloc):(type==LB_GVAR?vglast++:vtlast++);
1381 if (local < 0) l->ext = 1;
1383 if (gotext) l->ext = -1;
1384 if (l->value > 126) fatal("too many vars");
1385 if (nextToken() != ',') break;
1390 static void parseUsedVarList (void) {
1391 for (;;) {
1392 LabelInfo *l;
1394 if (nextToken() != TK_ID) fatal("identifier expected");
1395 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1396 l = findLabel(tstr);
1397 if (l == NULL) fatal("unknown label: '%s'", tstr);
1398 l->used = 1;
1399 if (nextToken() != ',') break;
1404 static void parseLocList (void) {
1405 for (;;) {
1406 LabelInfo *l;
1408 if (nextToken() != TK_ID) fatal("identifier expected");
1409 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1410 if (tstr[0] != '.') fatal("local identifier expected instead of '%s'", tstr);
1411 l = findLabel(tstr);
1412 if (l != NULL) fatal("can't redefine label as local: '%s'", tstr);
1413 l = addLabel(tstr);
1414 l->ext = 0;
1415 l->type = LB_SVAR;
1416 l->value = -1;
1417 if (nextToken() != '=') fatal("'=' expected");
1418 if (nextToken() != TK_NUM) fatal("number expected");
1419 l->value = tint;
1420 if (nextToken() != ',') break;
1425 static void parseConst (int ext, int gotext) {
1426 LabelInfo *l;
1428 if (nextToken() != TK_ID) fatal("identifier expected");
1429 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1430 if (tstr[0] == '.') fatal("invalid constant name: '%s'", tstr);
1431 l = findLabel(tstr);
1432 if (l != NULL) {
1433 if (gotext) {
1434 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1435 if (l->type != LB_CONST) fatal("can't change existing extern label type: '%s'", tstr);
1436 } else {
1437 fatal("constant must be unique: '%s'", tstr);
1440 if (l == NULL) {
1441 l = addLabel(tstr);
1442 l->type = LB_CONST;
1443 l->value = 0;
1445 if (!gotext) {
1446 if (nextToken() == '=') nextToken();
1447 if (token != TK_NUM) fatal("constant must be numeric");
1448 l->value = tint;
1450 if (ext) l->ext = 1;
1451 if (gotext) l->ext = -1;
1452 nextToken();
1456 static char procname[8192];
1457 static int proclocals = 0;
1458 static int procargs = 0;
1459 static int lastWasReturn = 0;
1462 static void parseProc (int ext) {
1463 LabelInfo *l, *a = NULL;
1464 int spt;
1466 lastWasReturn = 0;
1467 if (nextToken() != TK_ID) fatal("identifier expected");
1468 if (strcmp(tstr, ".") == 0) fatal("invalid label name: '@@'");
1469 if (tstr[0] == '.') fatal("invalid proc name: '%s'", tstr);
1470 if (procname[0]) fatal("unclosed proc: '%s'", procname);
1471 strcpy(procname, tstr);
1472 proclocals = 0;
1473 procargs = 0;
1474 freeLocalLabels(0); // vars and code
1476 l = findLabel(tstr);
1477 if (l != NULL) {
1478 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate proc label: '%s'", tstr);
1479 fixupLabelRefs(l, pc);
1480 } else {
1481 l = addLabel(tstr);
1482 l->type = LB_CODE;
1483 l->value = pc;
1485 if (ext) l->ext = 1;
1487 nextToken();
1489 while (token == TK_LABELDEF && (strcmp(tstr, "arg") == 0 || strcmp(tstr, "args") == 0)) {
1490 for (;;) {
1491 if (nextToken() != TK_ID) fatal("identifier expected");
1492 if (tstr[0] != '.') fatal("argument name must starts with '.'");
1493 l = findLabel(tstr);
1494 if (l != NULL) fatal("duplicate argument: '%s'", l->name);
1495 l = addLabel(tstr);
1496 l->type = LB_SVAR;
1497 l->value = 666;
1498 ++procargs;
1499 if (nextToken() != ',') break;
1501 a = labels; //HACK!
1502 // fix values
1503 // -1: return address, -2: last arg
1504 for (spt = -2, l = a; l->type == LB_SVAR; l = l->next) {
1505 l->value = spt;
1506 --spt;
1510 spt = -1; // first local
1511 while (token == TK_LABELDEF && (strcmp(tstr, "local") == 0 || strcmp(tstr, "locals") == 0)) {
1512 for (;;) {
1513 if (nextToken() != TK_ID) fatal("identifier expected");
1514 if (tstr[0] != '.') fatal("local variable name must starts with '.'");
1515 l = findLabel(tstr);
1516 if (l != NULL) fatal("duplicate local: '%s'", l->name);
1517 l = addLabel(tstr);
1518 l->type = LB_SVAR;
1519 l->value = spt--;
1520 ++proclocals;
1521 // fix args
1522 for (l = a; l != NULL && l->type == LB_SVAR; l = l->next) --(l->value);
1523 if (nextToken() != ',') break;
1527 if (proclocals > 0) {
1528 // allocate stack
1529 OperandInfo op0;
1531 setIntOperand(&op0, -proclocals);
1532 emitInstruction(VM_POP, &op0, NULL, NULL);
1537 static void parseEndP (void) {
1538 if (!procname[0]) fatal("'endp' without 'proc'");
1539 if (nextToken() != TK_ID) fatal("identifier expected");
1540 if (strcmp(procname, tstr) != 0) fatal("endp for '%s' in proc '%s'", tstr, procname);
1541 //if (!lastWasReturn) fatal("no 'return' in proc");
1542 nextToken();
1543 procname[0] = 0;
1544 freeLocalLabels(0);
1548 static void doReturn (void) {
1549 OperandInfo op0, op1, op2;
1551 if (!procname[0]) fatal("'return' without 'proc'");
1552 lastWasReturn = 1;
1553 op0.var = op1.var = op2.var = -1;
1554 if (hasOperand()) getOperand(&op2, 0); // result
1555 setIntOperand(&op0, proclocals);
1556 setIntOperand(&op1, procargs);
1557 emitInstruction(VM_RET, &op0, &op1, &op2);
1561 static void parseLabel (int gotext) {
1562 LabelInfo *l;
1564 if (gotext && tstr[0] == '.') fatal("can't declare local extern label: '%s'", tstr);
1565 if (strcmp(tstr, ".") == 0) {
1566 newTempLabel();
1567 nextToken();
1568 return;
1570 if (tstr[0] != '.' && tstr[0] != '@') {
1571 if (!gotext) {
1572 if (procname[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname);
1574 freeLocalLabels(0); // vars and code
1576 if (tstr[0] == '@') {
1577 char *d = tstr;
1579 while (*d++) d[-1] = d[0];
1580 d[-1] = 0;
1582 l = findLabel(tstr);
1583 if (l != NULL) {
1584 if (gotext) {
1585 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1586 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
1588 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate label: '%s'", tstr);
1589 fixupLabelRefs(l, pc);
1590 } else {
1591 l = addLabel(tstr);
1592 l->type = LB_CODE;
1593 l->value = pc;
1595 if (gotext) l->ext = -1;
1596 nextToken();
1600 static void parseDW (void) {
1601 for (;;) {
1602 LabelInfo *l = NULL;
1603 nextToken();
1604 if (token == TK_ID) {
1605 l = findLabel(tstr);
1607 if (l == NULL) {
1608 l = addLabel(tstr);
1609 l->type = LB_CODE;
1610 l->value = -1;
1611 //fatal("unknown label: '%s'", tstr);
1613 tint = l->value;
1614 } else if (token != TK_NUM) {
1615 fatal("number expected");
1617 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1618 emitByte(tint&0xff);
1619 emitByte((tint>>8)&0xff);
1620 if (l != NULL) {
1621 addLabelRef(l, pc-2);
1622 addExternRef(l, pc-2, 2);
1623 if (l->ext >= 0 && l->name[0] != '.') {
1624 switch (l->type) {
1625 case LB_GVAR: addGVarFixup(pc-2); break;
1626 case LB_TVAR: addTVarFixup(pc-2); break;
1630 if (nextToken() != ',') break;
1635 // terminator eaten
1636 static void parseAndPutString (int qch) {
1637 for (;;) {
1638 int ch = nextChar();
1640 //printf("[%c] [%c]\n", ch, qch);
1641 if (ch == EOF) fatal("unterminated string");
1642 if (qch == '"') {
1643 if (ch == qch) break;
1644 if (ch == '\\') {
1645 int n;
1647 ch = nextChar();
1648 if (ch == EOF) fatal("invalid escape");
1649 switch (ch) {
1650 case 'a': emitByte('\a'); break;
1651 case 'b': emitByte('\b'); break;
1652 case 'e': emitByte('\x1b'); break;
1653 case 'f': emitByte('\f'); break;
1654 case 'n': emitByte('\n'); break;
1655 case 'r': emitByte('\r'); break;
1656 case 't': emitByte('\t'); break;
1657 case 'v': emitByte('\v'); break;
1658 case '"': case '\'': case '\\': case ' ': emitByte(ch); break;
1659 case 'x':
1660 n = digit(nextChar(), 16);
1661 if (n < 0) fatal("invalid hex escape");
1662 ch = nextChar();
1663 if (ch == EOF) fatal("invalid hex escape");
1664 if (digit(ch, 16) >= 0) {
1665 n = n*16+digit(ch, 16);
1666 } else {
1667 ungetChar(ch);
1669 emitByte(n);
1670 break;
1671 default: fatal("invalid escape: '%c'", ch);
1673 } else {
1674 emitByte(ch);
1676 } else {
1677 if (ch == qch) {
1678 ch = nextChar();
1679 if (ch == EOF) return;
1680 if (ch == qch) {
1681 emitByte(ch);
1682 continue;
1684 ungetChar(ch);
1685 break;
1686 } else {
1687 emitByte(ch);
1694 static void parseDAscii (int zeroend) {
1695 for (;;) {
1696 LabelInfo *l;
1698 nextToken();
1699 if (token == TK_EOF) break;
1700 switch (token) {
1701 case '"': tokenWantFileName = 1; parseAndPutString(token); tokenWantFileName = 0; break;
1702 case '\'': tokenWantFileName = 1; parseAndPutString(token); tokenWantFileName = 0; break;
1703 case TK_ID:
1704 l = findLabel(tstr);
1706 if (l == NULL) fatal("unknown label: '%s'", tstr);
1707 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1708 l->used = 1;
1709 addExternRef(l, pc, 1);
1710 if (l->ext >= 0 && l->name[0] != '.') {
1711 switch (l->type) {
1712 case LB_GVAR: addGVarFixup(pc); break;
1713 case LB_TVAR: addTVarFixup(pc); break;
1716 tint = l->value;
1717 // fallthru
1718 case TK_NUM:
1719 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1720 emitByte(tint&0xff);
1721 break;
1722 default:
1723 fatal("number expected");
1725 if (nextToken() != ',') break;
1727 if (zeroend) emitByte(0);
1731 static void parsePublics (void) {
1732 for (;;) {
1733 LabelInfo *l;
1735 nextToken();
1736 if (token == TK_LABELDEF) {
1737 parseLabel(0);
1738 break;
1740 if (token != TK_ID) fatal("identifier expected");
1741 if (tstr[0] == '.') fatal("invalid label name: '%s'", tstr);
1742 l = findLabel(tstr);
1743 if (l != NULL) {
1744 l->ext = 1;
1745 } else {
1746 l = addLabel(tstr);
1747 l->ext = 1;
1748 l->type = LB_CODE;
1749 l->value = -1;
1751 if (nextToken() != ',') break;
1756 static void parseMacroDef (void) {
1757 MacroDef *mc;
1758 int c;
1759 char *text = NULL;
1760 int tpos = 0, tsize = 0;
1762 void addChar (int c) {
1763 if (tpos+1 > tsize) {
1764 int newsz = tsize+1024;
1765 char *nn = realloc(text, newsz);
1767 if (nn == NULL) fatal("out of memory");
1768 text = nn;
1769 tsize = newsz;
1771 text[tpos++] = c;
1774 if (nextToken() != TK_ID) fatal("macro name expected");
1775 if (tstr[0] == '.') fatal("macros can't be local: '%s'", tstr);
1776 for (mc = macros; mc != NULL; mc = mc->next) if (strcmp(mc->name, tstr) == 0) fatal("macros can't be redefined: '%s'", tstr);
1777 if ((mc = calloc(1, sizeof(MacroDef))) == NULL) fatal("out of memory");
1778 mc->next = macros;
1779 if ((mc->name = strdup(tstr)) == NULL) fatal("out of memory");
1780 macros = mc;
1781 // now parse macro args
1782 c = nextChar();
1783 ungetChar(c);
1784 skipSpaces(0); // no newlines allowed here
1785 c = nextChar();
1786 if (c != '\n') {
1787 // has args
1788 ungetChar(c);
1789 for (;;) {
1790 if (nextToken() != TK_ID) fatal("invalid macro argument definition");
1791 if (tstr[0] == '.') fatal("macro argument name must not start with dot: '%s'", tstr);
1792 addMacroArgDef(mc, tstr);
1793 skipSpaces(1);
1794 c = nextChar();
1795 if (c != ',') { ungetChar(c); break; }
1798 // now parse macro text
1799 for (;;) {
1800 int c1;
1802 c = nextChar();
1803 switch (c) {
1804 case EOF: fatal("incomplete macro definition: '%s'", mc->name);
1805 case '\n': // check for macro end
1806 addChar(c);
1807 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1808 if (c != 'e') { ungetChar(c); break; }
1809 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1810 if (c != 'n') { ungetChar(c); addChar('e'); break; }
1811 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1812 if (c != 'd') { ungetChar(c); addChar('e'); addChar('n'); break; }
1813 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1814 if (c != 'm') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); break; }
1815 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1816 if (c != 'a') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); break; }
1817 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1818 if (c != 'c') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); break; }
1819 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1820 if (c != 'r') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); break; }
1821 c = nextChar(); if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1822 if (c != 'o') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); break; }
1823 c = nextChar();
1824 if (c == EOF) goto macro_complete;
1825 if (!isspace(c) && c != ';' && c != '/') { ungetChar(c); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); addChar('o'); break; }
1826 else {
1827 skipSpaces(0); // no EOLs
1828 c = nextChar();
1829 if (c != '\n') fatal("invalid endmacro: '%s' (%d)", mc->name, c);
1830 goto macro_complete;
1832 break;
1833 case '"': // string
1834 addChar(c);
1835 tokenWantFileName = 1;
1836 for (;;) {
1837 c = nextChar();
1838 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1839 addChar(c);
1840 if (c == '"') break;
1841 if (c == '\\') {
1842 c = nextChar();
1843 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1844 addChar(c);
1847 tokenWantFileName = 0;
1848 break;
1849 case '\'': // string
1850 addChar(c);
1851 tokenWantFileName = 1;
1852 for (;;) {
1853 c = nextChar();
1854 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1855 addChar(c);
1856 if (c == '\'') {
1857 c = nextChar();
1858 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1859 if (c != '\'') { ungetChar(c); break; }
1860 addChar(c);
1863 tokenWantFileName = 0;
1864 break;
1865 case ';': // comment
1866 one_line_comment:
1867 addChar('\n');
1868 for (;;) {
1869 c = nextChar();
1870 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1871 if (c == '\n') break;
1873 break;
1874 case '/':
1875 c1 = nextChar();
1876 if (c1 == '/') goto one_line_comment; // comment
1877 if (c1 == '*') {
1878 // multiline comment
1879 for (;;) {
1880 c = nextChar();
1881 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1882 if (c == '*') {
1883 c = nextChar();
1884 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1885 if (c == '/') break;
1888 } else if (c1 == EOF) {
1889 fatal("incomplete macro definition: '%s'", mc->name);
1890 } else {
1891 addChar(c);
1892 addChar(c1);
1894 break;
1895 default:
1896 addChar(c);
1897 break;
1900 macro_complete:
1901 addChar('\0');
1902 mc->text = text;
1903 nextToken();
1907 static void parseMacroInvocation (MacroDef *mc) {
1908 MStrListItem *args = NULL;
1910 if (mc->argnames != NULL) {
1911 nextToken(); // skip macro name
1912 // collect macro arguments
1913 for (MStrListItem *a = mc->argnames; a != NULL; a = a->next) {
1914 int pos, c;
1916 switch (token) {
1917 case TK_EOF: fatal("macro argument expected: '%s'", mc->name);
1918 case TK_LABELDEF: fatal("macro argument can't be label: '%s'", mc->name);
1919 case TK_NUM: sprintf(tstr, "%d", tint); break;
1920 case TK_ID: break; // ok
1921 case '"': // string
1922 pos = 0;
1923 tstr[pos++] = token;
1924 tokenWantFileName = 1;
1925 for (;;) {
1926 c = nextChar();
1927 if (c == EOF) fatal("incomplete macro argument: '%s'", mc->name);
1928 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1929 tstr[pos++] = c;
1930 if (c == '"') break;
1931 if (c == '\\') {
1932 c = nextChar();
1933 if (c == EOF) fatal("incomplete macro argument: '%s'", mc->name);
1934 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1935 tstr[pos++] = c;
1938 tokenWantFileName = 0;
1939 tstr[pos] = 0;
1940 break;
1941 case '\'': // string
1942 pos = 0;
1943 tstr[pos++] = token;
1944 tokenWantFileName = 1;
1945 for (;;) {
1946 c = nextChar();
1947 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1948 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1949 tstr[pos++] = c;
1950 if (c == '\'') {
1951 c = nextChar();
1952 if (c == EOF) fatal("incomplete macro definition: '%s'", mc->name);
1953 if (c != '\'') { ungetChar(c); break; }
1954 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1955 tstr[pos++] = c;
1958 tokenWantFileName = 0;
1959 tstr[pos] = 0;
1960 break;
1961 default:
1962 if (token >= TK_VM_OPERATOR) break;
1963 pos = 0;
1964 tstr[pos++] = token;
1965 for (;;) {
1966 c = nextChar();
1967 if (c == EOF) break;//fatal("incomplete macro definition: '%s'", mc->name);
1968 if (pos >= MAX_TOKEN_LENGTH) fatal("macro argument too long: '%s'", mc->name);
1969 //TODO: better comment parsing
1970 if (c == ',' || c == ';' || c == '/' || c == '\n') { ungetChar(c); break; }
1971 tstr[pos++] = c;
1973 while (isspace(tstr[pos-1])) --pos;
1974 tstr[pos] = 0;
1975 break;
1976 //fatal("invalid macro argument: '%s'", mc->name);
1978 //fprintf(stderr, "arg [%s]: [%s]\n", mc->argnames->macname, tstr);
1979 args = addMacroArg(args, mc->argnames->macname, tstr);
1980 if (a->next != NULL) {
1981 if (nextToken() != ',') fatal("macro argument expected: '%s'", mc->name);
1985 // arguments collected
1986 openMacro(mc, args);
1987 nextToken();
1991 static void parseLabelDef () {
1992 LabelInfo *l;
1994 if (nextToken() != TK_ID) fatal("label name expected");
1996 l = findLabel(tstr);
1997 if (strcmp(tstr, ".") == 0) fatal("can't declare temp label");
1999 if (l != NULL) {
2000 // new
2001 if (l->ext < 0) fatal("can't declare extern label: '%s'", tstr);
2002 if (l->type != LB_CODE || l->value >= 0) fatal("can't redeclare label: '%s'", tstr);
2003 //fixupLabelRefs(l, pc);
2004 } else {
2005 l = addLabel(tstr);
2006 l->type = LB_CODE;
2007 l->value = -1;
2010 if (nextToken() == '=') {
2011 nextToken();
2012 //fprintf(stderr, "token=%d\n", token);
2013 if (token == '$') {
2014 int ch;
2016 //TODO: allow simple repeated math (with labels too)
2017 l->value = pc;
2018 skipSpaces(0); // not newlines
2019 ch = nextChar();
2020 if (ch == '-' || ch == '+') {
2021 nextToken();
2022 if (token != TK_NUM) fatal("number expected");
2023 if (ch == '-') tint = -tint;
2024 l->value += tint;
2025 //fprintf(stderr, "pc=%d; tint=%d; value=%d\n", pc, tint, l->value);
2026 } else {
2027 if (ch != EOF && ch != '\n') fatal("invalid label definition: '%s'", l->name);
2029 } else if (token == TK_NUM) {
2030 l->value = tint;
2031 } else {
2032 fatal("invalid label definition: '%s'", l->name);
2034 nextToken();
2035 } else {
2036 l->value = pc;
2038 if (l->value < 0) l->value &= 0xffff;
2039 fixupLabelRefs(l, l->value);
2043 static void process (void) {
2044 int gotext = 0;
2045 LabelInfo *l = addLabel("retval");
2047 l->type = LB_TVAR;
2048 l->value = VM_VARS_SIZE-1;
2050 memset(&labelTempBack, 0, sizeof(LabelInfo));
2051 memset(&labelTempFwd, 0, sizeof(labelTempFwd));
2053 labelTempBack.type = labelTempFwd.type = LB_CODE;
2054 labelTempBack.value = labelTempFwd.value = -1;
2056 procname[0] = 0;
2057 lastWasReturn = 0;
2058 nextToken();
2059 while (token != TK_EOF) {
2060 int opc;
2062 if (token == TK_LABELDEF) {
2063 if (strcmp(tstr, "extern") == 0) {
2064 if (gotext) fatal("double 'extern'");
2065 gotext = 1;
2066 nextToken();
2067 continue;
2069 // new label or operator
2070 if (strcmp(tstr, "include") == 0) {
2071 parseInlcude();
2072 continue;
2073 } else if (strcmp(tstr, "defloc") == 0) {
2074 if (gotext) fatal("extern labels must not be locals");
2075 parseLocList();
2076 } else if (strcmp(tstr, "defgvar") == 0) {
2077 parseVarList(LB_GVAR, 0, gotext);
2078 } else if (strcmp(tstr, "defevar") == 0) { // extern global
2079 parseVarList(LB_GVAR, -1, gotext);
2080 } else if (strcmp(tstr, "deftvar") == 0) {
2081 freeLocalLabels(1); // only vars
2082 vtloc = VM_VARS_SIZE;
2083 parseVarList(LB_TVAR, 0, gotext);
2084 } else if (strcmp(tstr, "defetvar") == 0) {
2085 freeLocalLabels(1); // only vars
2086 vtloc = VM_VARS_SIZE;
2087 parseVarList(LB_TVAR, -1, gotext);
2088 } else if (strcmp(tstr, "deflvar") == 0) {
2089 if (gotext) fatal("extern labels must not be locals");
2090 parseVarList(LB_TVAR, 1, gotext);
2091 } else if (strcmp(tstr, "public") == 0) {
2092 if (gotext) fatal("invalid extern label declaration");
2093 parsePublics();
2094 } else if (strcmp(tstr, "proc") == 0 || strcmp(tstr, "eproc") == 0) {
2095 if (gotext) fatal("invalid extern label declaration");
2096 parseProc(tstr[0] == 'e');
2097 } else if (strcmp(tstr, "endp") == 0) {
2098 if (gotext) fatal("invalid extern label declaration");
2099 parseEndP();
2100 } else if (strcmp(tstr, "const") == 0 || strcmp(tstr, "econst") == 0) {
2101 parseConst(tstr[0] == 'e', gotext);
2102 } else if (strcmp(tstr, "db") == 0) {
2103 if (gotext) fatal("invalid extern label declaration");
2104 parseDAscii(0);
2105 } else if (strcmp(tstr, "dw") == 0) {
2106 if (gotext) fatal("invalid extern label declaration");
2107 parseDW();
2108 } else if (strcmp(tstr, "da") == 0) {
2109 if (gotext) fatal("invalid extern label declaration");
2110 parseDAscii(0);
2111 } else if (strcmp(tstr, "dz") == 0) {
2112 if (gotext) fatal("invalid extern label declaration");
2113 parseDAscii(1);
2114 } else if (strcmp(tstr, "macro") == 0) {
2115 if (gotext) fatal("macros can't be external");
2116 parseMacroDef();
2117 } else if (strcmp(tstr, "used") == 0) {
2118 if (gotext) fatal("'used' labels can't be external");
2119 parseUsedVarList();
2120 } else if (strcmp(tstr, "gvarbase") == 0) {
2121 if (gotext) fatal("'gvarbase' can't be external");
2122 if (nextToken() != TK_NUM) fatal("gvarbase: number expected");
2123 vglast = tint;
2124 nextToken();
2125 } else if (strcmp(tstr, "tvarbase") == 0) {
2126 if (gotext) fatal("'tvarbase' can't be external");
2127 if (nextToken() != TK_NUM) fatal("tvarbase: number expected");
2128 vtlast = tint;
2129 nextToken();
2130 } else if (strcmp(tstr, "label") == 0) {
2131 if (gotext) fatal("'label' can't be external");
2132 parseLabelDef();
2133 } else {
2134 parseLabel(gotext);
2136 gotext = 0;
2137 continue;
2138 } else {
2139 if (gotext) {
2140 LabelInfo *l;
2142 if (token != TK_ID) fatal("label declaration expected after 'extern'");
2143 if (tstr[0] == '.') fatal("extern label can't be local: '%s'", tstr);
2144 l = findLabel(tstr);
2145 if (l != NULL) {
2146 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
2147 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
2148 } else {
2149 l = addLabel(tstr);
2150 l->type = LB_CODE;
2151 l->value = 0;
2152 l->ext = -1;
2154 nextToken();
2155 continue;
2158 // check for macro invocation
2159 if (token == TK_ID) {
2160 for (MacroDef *mc = macros; mc != NULL; mc = mc->next) {
2161 if (strcmp(mc->name, tstr) == 0) {
2162 parseMacroInvocation(mc);
2163 continue;
2168 if (token < TK_VM_OPERATOR) {
2169 //fprintf(stderr, "%d: [%s]\n", token, tstr);
2170 fatal("mnemonics expected");
2172 opc = token;
2173 if (opc < 600) opc -= TK_VM_OPERATOR;
2174 nextToken();
2175 lastWasReturn = 0;
2176 switch (opc) {
2177 case VM_ADD:
2178 case VM_SUB:
2179 case VM_MUL:
2180 case VM_DIV:
2181 case VM_MOD:
2182 case VM_BOR:
2183 case VM_XOR:
2184 case VM_AND:
2185 doMath(opc);
2186 break;
2187 case VM_JEQ:
2188 case VM_JNE:
2189 case VM_JLT:
2190 case VM_JLE:
2191 case VM_JGT:
2192 case VM_JGE:
2193 doJXX(opc);
2194 break;
2195 case VM_JMP:
2196 doBranch(opc);
2197 break;
2198 case VM_END:
2199 doNoOperands(opc);
2200 break;
2201 case VM_BSR:
2202 doBSR(opc);
2203 break;
2204 case VM_NEW:
2205 doNew(opc);
2206 break;
2207 case VM_BRK:
2208 doNoOperands(opc);
2209 break;
2210 case VM_SET:
2211 doSet(opc);
2212 break;
2213 case VM_GET:
2214 doGet(opc);
2215 break;
2216 case VM_PSH:
2217 doPush(opc);
2218 break;
2219 case VM_POP:
2220 doPop(opc);
2221 break;
2222 case VM_SWP:
2223 doSwap(opc);
2224 break;
2225 case VM_PCK:
2226 case VM_ROL:
2227 doPickRoll(opc);
2228 break;
2229 case VM_DPT:
2230 doDepth(opc);
2231 break;
2232 case VM_TID:
2233 doTId(opc);
2234 break;
2235 case VM_KIL:
2236 case VM_SUS:
2237 case VM_RES:
2238 doKillSusRes(opc);
2239 break;
2240 case VM_STA:
2241 doSta(opc);
2242 break;
2243 case VM_RXC:
2244 doRXC(opc);
2245 break;
2246 case VM_WXC:
2247 doWXC(opc);
2248 break;
2249 case VM_RST:
2250 doRST(opc);
2251 break;
2252 case VM_MGF:
2253 case VM_MGB:
2254 doMap(opc, 1);
2255 break;
2256 case VM_MSF:
2257 case VM_MSB:
2258 doMap(opc, 0);
2259 break;
2260 case VM_RET:
2261 if (procname[0]) doReturn(); else doRet(opc);
2262 break;
2263 case VMX_DRP:
2264 doDrop(VM_POP);
2265 break;
2266 case VMX_DUP:
2267 doNoOperands(VM_PSH);
2268 break;
2269 case VMX_RTN:
2270 doRet(opc);
2271 break;
2272 case VMX_RETURN:
2273 doReturn();
2274 break;
2275 default:
2276 fatal("not yet");
2279 if (procname[0]) fatal("'proc' without 'endp': '%s'", procname);
2283 ////////////////////////////////////////////////////////////////////////////////
2284 static int cfWriteByte (FILE *fl, int value) {
2285 unsigned char b;
2287 b = (value&0xff)^SECRET;
2288 if (fwrite(&b, 1, 1, fl) != 1) return -1;
2289 return 0;
2293 static int cfWriteWord (FILE *fl, int value) {
2294 if (cfWriteByte(fl, value) != 0) return -1;
2295 if (cfWriteByte(fl, value>>8) != 0) return -1;
2296 return 0;
2300 static int cfWriteCode (FILE *fl) {
2301 for (int f = 0; f < pc; ++f) if (cfWriteByte(fl, vmcode[f]) != 0) return -1;
2302 return 0;
2306 static int cfWriteRels (FILE *fl) {
2307 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) {
2308 if (cfWriteWord(fl, r->pc) != 0) return -1;
2310 return 0;
2314 static int cfWritePublicLabels (FILE *fl) {
2315 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2316 if (l->ext >= 0 && l->name[0]) {
2317 int len = strlen(l->name);
2319 if (cfWriteByte(fl, l->type|(l->ext>0?0x80:0)) != 0) return -1;
2321 switch (l->type) {
2322 case LB_GVAR:
2323 case LB_TVAR:
2324 if (cfWriteByte(fl, l->value) != 0) return -1;
2325 break;
2326 case LB_SVAR:
2327 case LB_CODE:
2328 case LB_CONST:
2329 if (cfWriteWord(fl, l->value) != 0) return -1;
2330 break;
2331 default:
2332 abort();
2335 if (len > 255) len = 255;
2336 if (cfWriteByte(fl, len) != 0) return -1;
2337 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
2340 return 0;
2344 static int cfWriteExtLabels (FILE *fl) {
2345 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2346 if (l->ext < 0 && l->name[0] && l->refs != NULL) {
2347 int len = strlen(l->name), rcnt = 0;
2349 if (cfWriteByte(fl, l->type) != 0) return -1;
2350 if (len > 255) len = 255;
2351 if (cfWriteByte(fl, len) != 0) return -1;
2352 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
2354 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) ++rcnt;
2355 if (cfWriteWord(fl, rcnt) != 0) return -1;
2356 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) {
2357 if (cfWriteByte(fl, r->size) != 0) return -1;
2358 if (cfWriteWord(fl, r->pc) != 0) return -1;
2362 return 0;
2366 static int cfWriteVarFixups (FILE *fl, const VarFixup *list) {
2367 int cnt = 0;
2369 for (const VarFixup *f = list; f != NULL; f = f->next) ++cnt;
2370 if (cfWriteWord(fl, cnt) != 0) return -1;
2371 for (const VarFixup *f = list; f != NULL; f = f->next) {
2372 if (cfWriteWord(fl, f->pc) != 0) return -1;
2374 return 0;
2378 static int writeCodeFile (FILE *fl) {
2379 static const char *sign = "AVM2";
2380 int lcnt = 0, elcnt = 0;
2381 int rcnt = 0;
2383 relrefs = lrefRemoveDups(relrefs);
2384 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) ++rcnt;
2385 for (LabelInfo *l = labels; l != NULL; l = l->next) {
2386 if (l->name[0]) {
2387 if (l->ext >= 0) ++lcnt;
2388 if (l->ext < 0) {
2389 l->refs = lrefRemoveDups(l->refs);
2390 if (l->refs != NULL) ++elcnt;
2394 fprintf(stderr, "%d bytes of code, %d public labels, %d fixups, %d externs; maxgvar: %d, maxtvar: %d\n", pc, lcnt, rcnt, elcnt, vglast, vtlast);
2396 if (fwrite(sign, 4, 1, fl) != 1) return -1;
2397 // code size
2398 if (cfWriteWord(fl, pc) != 0) return -1;
2399 // number of fixups
2400 if (cfWriteWord(fl, rcnt) != 0) return -1;
2401 // number of extern labels
2402 if (cfWriteWord(fl, elcnt) != 0) return -1;
2403 // number of labels
2404 if (cfWriteWord(fl, lcnt) != 0) return -1;
2405 // last used global
2406 if (cfWriteWord(fl, vglast) != 0) return -1;
2407 // last used thread local
2408 if (cfWriteWord(fl, vtlast) != 0) return -1;
2410 if (cfWriteCode(fl) != 0) return -1;
2411 if (cfWriteRels(fl) != 0) return -1;
2412 if (cfWriteExtLabels(fl) != 0) return -1;
2413 if (cfWritePublicLabels(fl) != 0) return -1;
2414 if (cfWriteVarFixups(fl, gvfixes) != 0) return -1;
2415 if (cfWriteVarFixups(fl, tvfixes) != 0) return -1;
2417 return 0;
2421 ////////////////////////////////////////////////////////////////////////////////
2422 #ifdef _WIN32
2423 # include "cmdline.c"
2424 #endif
2427 ////////////////////////////////////////////////////////////////////////////////
2428 int main (int argc, char *argv[]) {
2429 #ifdef _WIN32
2430 cmdLineParse();
2431 argc = k8argc;
2432 argv = k8argv;
2433 #endif
2435 for (int f = 1; f < argc; ++f) {
2436 if (strcmp(argv[f], "-Wno") == 0) {
2437 optWarnings = 0;
2438 for (int c = f+1; c < argc; ++c) argv[c-1] = argv[c];
2439 argv[--argc] = NULL;
2440 --f;
2441 continue;
2445 if (argc != 3) {
2446 fprintf(stderr, "usage: awasm infile outfile\n");
2447 return 1;
2449 openFile(argv[1]);
2450 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
2451 process();
2452 while (ictx != NULL) closeFile();
2453 freeLocalLabels(0); // vars and code
2454 checkLabels();
2455 //if (argc > 3) dumpGlobalVars(argv[3]);
2457 FILE *fo = fopen(argv[2], "wb");
2458 int res;
2460 if (fo == NULL) { fprintf(stderr, "FATAL: can't create output file: '%s'\n", argv[2]); return 1; }
2461 res = writeCodeFile(fo);
2462 if (fclose(fo) != 0) res = -1;
2463 if (res != 0) {
2464 fprintf(stderr, "FATAL: error writing output file: '%s'\n", argv[2]);
2465 unlink(argv[2]);
2466 return 1;
2469 freeLabels();
2470 freeLabelRefList(relrefs);
2471 freeMacroList();
2472 return 0;