alot of things changed in code file format; preparing for room local scripts
[awish.git] / src / awasm.c
blob105a2df52b9d56ceef6eb316ada1bbc39d63741b
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 #include <ctype.h>
16 #include <stdarg.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
23 #include "vm.h"
26 ////////////////////////////////////////////////////////////////////////////////
27 #define SECRET (42)
30 ////////////////////////////////////////////////////////////////////////////////
31 enum {
32 LB_GVAR,
33 LB_TVAR,
34 LB_SVAR,
35 LB_CODE,
36 LB_CONST
40 static const char *ltnames[] = {
41 "GVAR",
42 "TVAR",
43 "SVAR",
44 "CODE",
45 "CONST"
49 ////////////////////////////////////////////////////////////////////////////////
50 #define MAX_TOKEN_LENGTH (256)
52 enum {
53 TK_EOF = -1,
54 TK_ID = 256,
55 TK_NUM,
56 TK_LABELDEF,
58 TK_VM_OPERATOR = 400
62 enum {
63 VMX_DRP = 600,
64 VMX_DUP,
65 VMX_RTN,
66 VMX_RETURN
70 ////////////////////////////////////////////////////////////////////////////////
71 static uint8_t vmcode[65536];
72 static int pc = 0;
75 ////////////////////////////////////////////////////////////////////////////////
76 typedef struct InputContext {
77 struct InputContext *prev;
78 FILE *fl;
79 char *fname;
80 int lineno;
81 int ucnt;
82 int uca[4];
83 } InputContext;
86 static InputContext *ictx = NULL;
87 static int token;
88 static char tstr[MAX_TOKEN_LENGTH+1];
89 static int tint;
90 static int incLevel = 0;
93 ////////////////////////////////////////////////////////////////////////////////
94 static __attribute__((__noreturn__)) __attribute__((format(printf, 1, 2))) void fatal (const char *fmt, ...) {
95 va_list ap;
97 if (ictx != NULL) {
98 fprintf(stderr, "FATAL (line %d, file '%s'): ", ictx->lineno, ictx->fname);
99 } else {
100 fprintf(stderr, "FATAL: ");
102 va_start(ap, fmt);
103 vfprintf(stderr, fmt, ap);
104 va_end(ap);
105 fprintf(stderr, "\n");
106 exit(1);
110 ////////////////////////////////////////////////////////////////////////////////
111 static void openFile (const char *fname) {
112 InputContext *ic = malloc(sizeof(InputContext));
114 if (ic == NULL) abort();
115 ic->fl = fopen(fname, "r");
116 if (ic->fl == NULL) fatal("can't open file: '%s'", fname);
117 ic->prev = ictx;
118 ic->fname = strdup(fname);
119 #ifdef _WIN32
120 for (char *t = ic->fname; *t; ++t) if (*t == '\\') *t = '/';
121 #endif
122 ic->lineno = 1;
123 ic->ucnt = 0;
124 ictx = ic;
125 for (int f = 0; f < incLevel; ++f) fputc(' ', stderr);
126 ++incLevel;
127 fprintf(stderr, "compiling: %s\n", ic->fname);
131 static void closeFile (void) {
132 if (ictx != NULL) {
133 InputContext *ic = ictx;
135 ictx = ic->prev;
136 fclose(ic->fl);
137 if (ic->fname) free(ic->fname);
138 free(ic);
139 --incLevel;
141 if (ictx != NULL) {
142 for (int f = 0; f < incLevel-1; ++f) fputc(' ', stderr);
143 fprintf(stderr, "compiling: %s\n", ictx->fname);
150 ////////////////////////////////////////////////////////////////////////////////
151 static void ungetChar (int c) {
152 if (c != EOF && ictx != NULL) {
153 if (ictx->ucnt >= 4) fatal("too many unread chars");
154 ictx->uca[ictx->ucnt++] = c;
159 static int tokenWantFileName = 0;
161 static int nextChar (void) {
162 int c;
164 if (ictx == NULL) return EOF;
165 if (ictx->ucnt > 0) {
166 c = ictx->uca[--ictx->ucnt];
167 } else {
168 c = fgetc(ictx->fl);
169 if (c == EOF) {
170 closeFile();
171 c = (ictx==NULL ? EOF : '\n');
172 } else {
173 if (c == '\n') ++(ictx->lineno);
176 if (!tokenWantFileName) {
177 if (c >= 'A' && c <= 'Z') c += 32; // tolower
179 return c;
183 static void skipSpaces (void) {
184 for (;;) {
185 int c = nextChar();
187 if (c == EOF) return;
188 if (c == ';') {
189 do { c = nextChar(); } while (c != EOF && c != '\n');
190 continue;
192 if (c == '/') {
193 int c1 = nextChar();
195 if (c1 == '/') {
196 do { c = nextChar(); } while (c != EOF && c != '\n');
197 continue;
199 if (c1 == '*') {
200 for (;;) {
201 c = nextChar();
202 if (c == EOF) break;
203 if (c == '*') {
204 c = nextChar();
205 if (c == '/') break;
208 continue;
210 ungetChar(c1);
211 ungetChar(c);
212 break;
214 if (c > 32) { ungetChar(c); break; }
219 static int digit (int c, int base) {
220 if (c == EOF) return -1;
221 if (c >= 'a' && c <= 'z') c -= 32;
222 if (c > '9' && c < 'A') return -1;
223 if (c > '9') c -= 7;
224 c -= '0';
225 if (c < 0 || c >= base) return -1;
226 return c;
230 static void getNumber (int c) {
231 int base = 10;
233 token = TK_NUM;
234 tint = 0;
235 if (c == '0') {
236 c = nextChar();
237 if (c == EOF) return;
238 if (!isdigit(c)) {
239 switch (c) {
240 case 'b': case 'B': base = 2; break;
241 case 'o': case 'O': base = 8; break;
242 case 'd': case 'D': base = 10; break;
243 case 'x': case 'X': base = 16; break;
244 default:
245 if (isalpha(c)) fatal("invalid number");
246 if (c != EOF) ungetChar(c);
247 return;
249 c = nextChar();
250 if (digit(c, base) < 0) fatal("invalid number");
254 for (;;) {
255 int d = digit(c, base);
257 if (d < 0) break;
258 tint = (tint*base)+d;
259 if (tint > 32767) fatal("number constant too big");
260 c = nextChar();
262 if (c != EOF && isalpha(c)) fatal("invalid number");
263 if (c != EOF) ungetChar(c);
267 static int nextToken (void) {
268 int c;
270 memset(tstr, 0, sizeof(tstr));
271 tint = 0;
272 token = TK_EOF;
273 skipSpaces();
274 if (ictx == NULL) return TK_EOF;
275 c = nextChar();
276 if (c == EOF) return TK_EOF;
277 if (c >= 127) fatal("invalid char (%d)", c);
278 tstr[0] = c;
279 tstr[1] = 0;
281 if (isalpha(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
282 // id or label
283 int f = 1;
285 for (;;) {
286 c = nextChar();
287 if (isalnum(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
288 if (f >= MAX_TOKEN_LENGTH-1) fatal("identifier too long");
289 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
290 tstr[f++] = c;
291 } else {
292 //if (c != ';' && c > 32) fatal("invalid identifier");
293 break;
296 tstr[f] = 0;
297 if (!isalnum(tstr[0]) && !tstr[1]) {
298 if (c != EOF && c > 32) ungetChar(c);
299 token = tstr[0];
300 return token;
302 token = TK_ID;
303 // label definition?
304 if (!tokenWantFileName) {
305 if (c == ':') {
306 token = TK_LABELDEF;
307 } else if (c <= 32 && c != '\n') {
308 for (;;) {
309 c = nextChar();
310 if (c == ':') { token = TK_LABELDEF; break; }
311 if (c > 32) ungetChar(c);
312 if (c == EOF || c > 32) break;
314 } else {
315 if (c != EOF && c > 32) ungetChar(c);
317 // vm mnemonics?
318 if (f == 3) {
319 for (f = 0; vmOpNames[f]; ++f) {
320 if (strcmp(tstr, vmOpNames[f]) == 0) {
321 if (token == TK_LABELDEF) fatal("invalid label: '%s'", tstr);
322 token = TK_VM_OPERATOR+f;
323 break;
326 if (token < TK_VM_OPERATOR) {
327 if (strcmp(tstr, "drp") == 0) token = VMX_DRP;
328 else if (strcmp(tstr, "dup") == 0) token = VMX_DUP;
329 else if (strcmp(tstr, "rtn") == 0) token = VMX_RTN;
331 } else if (strcmp(tstr, "return") == 0) {
332 token = VMX_RETURN;
335 } else if (c == '-' || c == '+') {
336 int neg = (c=='-');
338 token = c;
339 if ((c = nextChar()) != EOF) {
340 if (isdigit(c)) {
341 getNumber(c);
342 if (neg) tint = -tint;
343 } else {
344 ungetChar(c);
347 } else if (isdigit(c)) {
348 // number
349 getNumber(c);
350 } else {
351 // delimiter
352 token = c;
354 return token;
358 ////////////////////////////////////////////////////////////////////////////////
359 typedef struct LabelRefInfo {
360 struct LabelRefInfo *next;
361 int pc;
362 int size;
363 } LabelRefInfo;
366 typedef struct LabelInfo {
367 struct LabelInfo *next;
368 char *name;
369 int type; // LB_XXXX
370 int value; // code && <0: not defined yet (forward ref)
371 int ext; // extern (<0), public(>0), normal(0)
372 LabelRefInfo *fixes; // for undefined code labels
373 LabelRefInfo *refs; // for externals
374 int used;
375 } LabelInfo;
378 static LabelInfo *labels = NULL;
379 static LabelRefInfo *relrefs = NULL; // info for relocaions
381 static int vglast = 0;
382 static int vtlast = 0;
383 static int vtloc = VM_VARS_SIZE-1; // local thread (var 126 is used for internal thing)
386 static LabelRefInfo *lrefRemoveDups (LabelRefInfo *list) {
387 LabelRefInfo *p = NULL, *c = list;
389 list = NULL;
390 while (c != NULL) {
391 int dup = 0;
393 if (list != NULL) {
394 for (LabelRefInfo *r = list; r != c; r = r->next) if (r->pc == c->pc && r->size == c->size) { dup = 1; break; }
397 if (dup) {
398 // remove
399 LabelRefInfo *n = c->next;
401 if (p != NULL) p->next = n;
402 free(c);
403 c = n;
404 } else {
405 // keep
406 if (p == NULL) list = c;
407 p = c;
408 c = c->next;
411 return list;
415 // returns new head (created item)
416 static LabelRefInfo *addLabelRefToList (LabelRefInfo *list, int pc) {
417 LabelRefInfo *res;
419 res = calloc(1, sizeof(LabelRefInfo));
420 if (res == NULL) fatal("out of memory");
421 res->next = list;
422 res->pc = pc;
423 res->size = 2;
424 return res;
428 static void freeLabelRefList (LabelRefInfo *list) {
429 while (list != NULL) {
430 LabelRefInfo *r = list;
432 list = r->next;
433 free(r);
438 static void addExternRef (LabelInfo *l, int pc, int size) {
439 if (l != NULL && l->ext < 0) {
440 if (size <= 0) {
441 size = (l->type == LB_CODE || l->type == LB_CONST || l->type == LB_SVAR) ? 2 : 1;
443 l->refs = addLabelRefToList(l->refs, pc);
444 l->refs->size = size;
449 static void addLabelRef (LabelInfo *l, int pc) {
450 if (l != NULL) {
451 l->used = 1;
452 addExternRef(l, pc, -1);
453 if (l->type == LB_CODE && l->ext >= 0) {
454 // record fixup info for undefined code labels
455 if (l->value < 0) l->fixes = addLabelRefToList(l->fixes, pc);
456 // record reference info for code labels
457 relrefs = addLabelRefToList(relrefs, pc);
463 static void fixupLabelRefs (LabelInfo *l, int pc) {
464 if (l != NULL) {
465 l->used = 1;
466 l->value = pc;
467 for (LabelRefInfo *fix = l->fixes; fix != NULL; fix = fix->next) {
468 vmcode[fix->pc+0] = (l->value)&0xff;
469 vmcode[fix->pc+1] = ((l->value)>>8)&0xff;
471 freeLabelRefList(l->fixes);
472 l->fixes = NULL;
477 static void checkLabels (void) {
478 for (LabelInfo *l = labels; l != NULL; l = l->next) {
479 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
480 if (!l->used) { l->used = -1; fprintf(stderr, "WARNING: unused %s label '%s'\n", ltnames[l->type], l->name); }
485 static void freeLabels (void) {
486 checkLabels();
487 while (labels) {
488 LabelInfo *l = labels;
490 labels = l->next;
491 freeLabelRefList(l->fixes);
492 freeLabelRefList(l->refs);
493 free(l->name);
494 free(l);
499 static void freeLocalLabels (int onlyVars) {
500 LabelInfo *p = NULL, *l = labels;
502 while (l != NULL) {
503 LabelInfo *n = l->next;
505 if ((!onlyVars || l->type != LB_CODE) && l->name[0] == '.') {
506 if (l->type == LB_CODE && l->ext >= 0 && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
507 if (!l->used) { l->used = -1; fprintf(stderr, "WARNING: unused %s label '%s'\n", ltnames[l->type], l->name); }
508 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
509 freeLabelRefList(l->fixes);
510 freeLabelRefList(l->refs);
511 free(l->name);
512 free(l);
513 if (p != NULL) p->next = n; else labels = n;
514 } else {
515 p = l;
517 l = n;
522 static LabelInfo *findLabel (const char *name) {
523 if (name && name[0] == '@') ++name;
524 if (!name || !name[0]) return NULL;
525 for (LabelInfo *l = labels; l != NULL; l = l->next) if (strcmp(l->name, name) == 0) return l;
526 return NULL;
530 static LabelInfo *addLabel (const char *name) {
531 LabelInfo *l;
533 if (!name || !name[0]) abort();
534 //if (strcmp(name, "item_glovesg") == 0) fatal("!!!");
535 if (findLabel(name)) fatal("duplicate label: '%s'", name);
536 l = malloc(sizeof(LabelInfo));
537 if (l == NULL) fatal("out of memory");
538 l->name = strdup(name);
539 if (l->name == NULL) fatal("out of memory");
540 l->type = LB_CODE;
541 l->value = -1;
542 l->ext = 0;
543 l->fixes = NULL;
544 l->refs = NULL;
545 l->next = labels;
546 labels = l;
547 return l;
551 ////////////////////////////////////////////////////////////////////////////////
552 typedef struct {
553 LabelInfo *l; // !=NULL: label
554 int var; // [...]
555 int value; // if l==NULL
556 int vartype; // 0: global; 1: tlocal; 2: stack
557 } OperandInfo;
560 static void setIntOperand (OperandInfo *op, int value) {
561 op->l = NULL;
562 op->var = 0;
563 op->value = value;
564 op->vartype = -1;
568 static void setLabelOperand (OperandInfo *op, LabelInfo *l) {
569 op->l = l;
570 op->var = 0;
571 op->value = 0;
572 op->vartype = -1;
576 static void getOperand (OperandInfo *op, int wantCode) {
577 if (token == TK_ID) {
578 LabelInfo *l = findLabel(tstr);
580 if (l == NULL) {
581 // new code label
582 l = addLabel(tstr);
583 l->type = LB_CODE;
584 l->value = -1;
585 } else {
586 if (wantCode && l->type != LB_CODE) fatal("code offset expected");
588 setLabelOperand(op, l);
589 nextToken();
590 return;
593 if (token == TK_NUM) {
594 if (wantCode) fatal("code offset expected");
595 setIntOperand(op, tint);
596 nextToken();
597 return;
600 if (token == '[') {
601 //if (wantCode) fatal("code offset expected");
602 nextToken();
603 if (token == TK_ID) {
604 LabelInfo *l = findLabel(tstr);
606 if (l == NULL || l->type == LB_CODE) fatal("unknown variable: '%s'", tstr);
607 setLabelOperand(op, l);
608 } else if (token == '@' || token == '.') {
609 int loc = token;
611 if (nextToken() != TK_NUM) fatal("index expected");
612 setIntOperand(op, tint);
613 if (loc != '.') {
614 // not stack
615 if (tint < 0 || tint > 126) fatal("invalid variable index");
616 op->vartype = loc=='@' ? 0 : 1;
617 } else {
618 // stack
619 op->vartype = 2;
621 } else if (token == TK_NUM) {
622 // local
623 if (tint < 0 || tint > 126) fatal("invalid variable index");
624 setIntOperand(op, tint);
625 op->vartype = 1;
626 } else {
627 fatal("invalid operand");
629 op->var = 1;
630 if (nextToken() != ']') fatal("']' expected");
631 nextToken();
632 return;
635 fprintf(stderr, "*%d [%s] %d\n", token, tstr, tint);
636 fatal("invalid operand");
640 static inline int hasOperand (void) {
641 return (token != TK_EOF && token != TK_LABELDEF && token < TK_VM_OPERATOR);
645 ////////////////////////////////////////////////////////////////////////////////
646 static void emitByte (int b) {
647 if (b < 0 || b > 255) fatal("internal error");
648 if (pc >= 32768) fatal("code too big");
649 vmcode[pc++] = b;
653 static void emitOpCode (int opc, int opcount) {
654 if (opc < 0 || opc > 63 || opcount < 0 || opcount > 3) fatal("internal error");
655 emitByte(opc|(opcount<<6));
660 static void emitInteger (int val) {
661 emitByte(255); // special
662 emitByte(val&0xff);
663 emitByte((val>>8)&0xff);
668 static void emitOperand (OperandInfo *op) {
669 if (!op || op->var < 0) return;
671 if (op->var > 0) {
672 // variable
673 if (op->l) {
674 // from label
675 op->l->used = 1;
676 switch (op->l->type) {
677 case LB_GVAR: addLabelRef(op->l, pc); emitByte(op->l->value|0x80); break;
678 case LB_TVAR: addLabelRef(op->l, pc); emitByte(op->l->value); break;
679 case LB_SVAR:
680 emitByte(127); // special
681 addLabelRef(op->l, pc);
682 emitByte(op->l->value&0xff);
683 emitByte((op->l->value>>8)&0xff);
684 break;
685 default: fatal("internal error");
687 } else {
688 // from value
689 switch (op->vartype) {
690 case 0: emitByte(op->value|0x80); break; // global
691 case 1: emitByte(op->value); break; // tlocal
692 case 2: // stack
693 emitByte(127); // special
694 emitByte(op->value&0xff);
695 emitByte((op->value>>8)&0xff);
696 break;
697 default: fatal("internal error");
700 return;
703 if (op->var == 0) {
704 // immediate
705 emitByte(255); // special
706 if (op->l) {
707 // from label
708 op->l->used = 1;
709 addLabelRef(op->l, pc);
710 emitByte(op->l->value&0xff);
711 emitByte((op->l->value>>8)&0xff);
712 } else {
713 // direct
714 if (op->value < -32767 || op->value > 32767) fatal("invalid value");
715 emitByte(op->value&0xff);
716 emitByte((op->value>>8)&0xff);
722 static void emitInstruction (int opc, OperandInfo *op0, OperandInfo *op1, OperandInfo *op2) {
723 int ocnt = 0;
725 if (op0 && op0->var >= 0) ++ocnt;
726 if (ocnt == 1 && op1 && op1->var >= 0) ++ocnt;
727 if (ocnt == 2 && op2 && op2->var >= 0) ++ocnt;
728 emitOpCode(opc, ocnt);
729 if (ocnt > 0) emitOperand(op0);
730 if (ocnt > 1) emitOperand(op1);
731 if (ocnt > 2) emitOperand(op2);
735 ////////////////////////////////////////////////////////////////////////////////
736 static void doNoOperands (int opcode) {
737 emitInstruction(opcode, NULL, NULL, NULL);
741 static void doMath (int opcode) {
742 OperandInfo op0, op1, op2;
744 op0.var = op1.var = op2.var = -1;
745 if (hasOperand()) {
746 getOperand(&op0, 0);
747 if (token == ',') {
748 nextToken();
749 getOperand(&op1, 0);
750 if (token == ',') {
751 nextToken();
752 getOperand(&op2, 0);
753 if (op2.var != 1) fatal("variable expected as third operand");
754 } else {
755 if (op0.var != 1) fatal("variable expected as first operand");
759 emitInstruction(opcode, &op0, &op1, &op2);
763 static void doJXX (int opcode) {
764 OperandInfo op0, op1, op2;
766 op0.var = op1.var = op2.var = -1;
767 if (hasOperand()) {
768 getOperand(&op0, 1);
769 if (token == ',') {
770 nextToken();
771 getOperand(&op1, 0);
772 if (token == ',') {
773 nextToken();
774 getOperand(&op2, 0);
778 emitInstruction(opcode, &op0, &op1, &op2);
782 static void doBranch (int opcode) {
783 OperandInfo op0;
785 op0.var = -1;
786 if (hasOperand()) {
787 getOperand(&op0, 1);
789 emitInstruction(opcode, &op0, NULL, NULL);
793 static void doBSR (int opcode) {
794 OperandInfo op0, op1, op2;
796 op0.var = op1.var = op2.var = -1;
797 if (hasOperand()) {
798 getOperand(&op0, 1);
799 if (token == ',') {
800 nextToken();
801 getOperand(&op1, 0);
802 if (token == ',') {
803 nextToken();
804 getOperand(&op2, 0);
808 emitInstruction(opcode, &op0, &op1, &op2);
812 static void doMap (int opcode, int lastMBV) {
813 OperandInfo op0, op1, op2;
815 op0.var = op1.var = op2.var = -1;
816 if (hasOperand()) {
817 getOperand(&op0, 0);
818 if (token == ',') {
819 nextToken();
820 getOperand(&op1, 0);
821 if (token == ',') {
822 nextToken();
823 getOperand(&op2, 0);
827 emitInstruction(opcode, &op0, &op1, &op2);
831 static void doRST (int opcode) {
832 OperandInfo op0, op1, op2;
834 op0.var = op1.var = op2.var = -1;
835 if (hasOperand()) {
836 getOperand(&op0, 0);
837 if (token == ',') {
838 nextToken();
839 getOperand(&op1, 0);
840 if (token == ',') {
841 nextToken();
842 getOperand(&op2, 0);
846 emitInstruction(opcode, &op0, &op1, &op2);
850 static void doSet (int opcode) {
851 OperandInfo op0, op1, op2;
853 op0.var = op1.var = op2.var = -1;
854 getOperand(&op0, 0);
855 if (token == ',') {
856 nextToken();
857 getOperand(&op1, 0);
858 if (token == ',') {
859 nextToken();
860 getOperand(&op2, 0);
863 if (op2.var < 0 && op0.var != 1) fatal("first operand should be var");
864 emitInstruction(opcode, &op0, &op1, &op2);
868 static void doDrop (int opcode) {
869 OperandInfo op0;
871 op0.var = -1;
872 if (hasOperand()) getOperand(&op0, 0);
873 if (op0.var > 0) fatal("number expected");
874 if (op0.value < 0) fatal("positive number expected");
875 emitInstruction(opcode, &op0, NULL, NULL);
879 static void doPush (int opcode) {
880 OperandInfo op0, op1, op2;
882 op0.var = op1.var = op2.var = -1;
883 getOperand(&op0, 0);
884 if (token == ',') {
885 nextToken();
886 getOperand(&op1, 0);
887 if (token == ',') {
888 nextToken();
889 getOperand(&op2, 0);
892 emitInstruction(opcode, &op0, &op1, &op2);
896 static void doPop (int opcode) {
897 OperandInfo op0, op1, op2;
899 op0.var = op1.var = op2.var = -1;
900 if (hasOperand()) {
901 getOperand(&op0, 0);
902 if (token == ',') {
903 nextToken();
904 getOperand(&op1, 0);
905 if (token == ',') {
906 nextToken();
907 getOperand(&op2, 0);
911 emitInstruction(opcode, &op0, &op1, &op2);
915 static void doSwap (int opcode) {
916 OperandInfo op0, op1;
918 op0.var = op1.var = -1;
919 if (hasOperand()) {
920 getOperand(&op0, 0);
921 if (token == ',') {
922 nextToken();
923 getOperand(&op1, 0);
926 emitInstruction(opcode, &op0, &op1, NULL);
930 static void doDepth (int opcode) {
931 OperandInfo op0;
933 op0.var = -1;
934 if (hasOperand()) getOperand(&op0, 0);
935 emitInstruction(opcode, &op0, NULL, NULL);
939 static void doPickRoll (int opcode) {
940 OperandInfo op0, op1;
942 op0.var = op1.var = -1;
943 if (hasOperand()) {
944 getOperand(&op0, 0);
945 if (token == ',') {
946 nextToken();
947 getOperand(&op1, 0);
948 if (op1.var != 1) fatal("second argument must be variable");
951 emitInstruction(opcode, &op0, &op1, NULL);
955 static void doNew (int opcode) {
956 OperandInfo op0, op1;
958 op0.var = op1.var = -1;
959 if (hasOperand()) {
960 getOperand(&op0, 0);
961 if (token == ',') {
962 nextToken();
963 getOperand(&op1, 0);
966 emitInstruction(opcode, &op0, &op1, NULL);
970 static void doTId (int opcode) {
971 OperandInfo op0;
973 op0.var = -1;
974 if (hasOperand()) getOperand(&op0, 0);
975 emitInstruction(opcode, &op0, NULL, NULL);
979 static void doKillSusRes (int opcode) {
980 OperandInfo op0;
982 op0.var = -1;
983 if (hasOperand()) getOperand(&op0, 0);
984 emitInstruction(opcode, &op0, NULL, NULL);
988 static void doSta (int opcode) {
989 OperandInfo op0, op1;
991 op0.var = op1.var = -1;
992 if (hasOperand()) {
993 getOperand(&op0, 0);
994 if (token == ',') {
995 nextToken();
996 getOperand(&op1, 0);
997 if (op1.var != 1) fatal("variable expected");
1000 emitInstruction(opcode, &op0, &op1, NULL);
1004 static void doGet (int opcode) {
1005 OperandInfo op0, op1, op2;
1007 op0.var = op1.var = op2.var = -1;
1008 getOperand(&op0, 0);
1009 if (token != ',') fatal("at least two operands expected");
1010 nextToken();
1011 getOperand(&op1, 0);
1012 if (token == ',') {
1013 nextToken();
1014 getOperand(&op2, 0);
1015 if (op2.var != 1) fatal("variable expected as third operand");
1017 emitInstruction(opcode, &op0, &op1, &op2);
1021 static void doRXC (int opcode) {
1022 OperandInfo op0, op1;
1024 op0.var = op1.var = -1;
1025 if (hasOperand()) {
1026 getOperand(&op0, 0);
1027 if (token == ',') {
1028 nextToken();
1029 getOperand(&op1, 0);
1030 if (op1.var != 1) fatal("variable expected as second operand");
1033 emitInstruction(opcode, &op0, &op1, NULL);
1037 static void doWXC (int opcode) {
1038 OperandInfo op0, op1;
1040 op0.var = op1.var = -1;
1041 if (hasOperand()) {
1042 getOperand(&op0, 0);
1043 if (token == ',') {
1044 nextToken();
1045 getOperand(&op1, 0);
1048 emitInstruction(opcode, &op0, &op1, NULL);
1052 static void doRet (int opcode) {
1053 OperandInfo op0, op1, op2;
1055 op0.var = op1.var = op2.var = -1;
1056 if (hasOperand()) {
1057 getOperand(&op0, 0);
1058 if (token != ',') fatal("at least two operands expected");
1059 nextToken();
1060 getOperand(&op1, 0);
1061 if (token == ',') {
1062 nextToken();
1063 getOperand(&op2, 0);
1066 emitInstruction(opcode, &op0, &op1, &op2);
1070 ////////////////////////////////////////////////////////////////////////////////
1071 static void parseInlcude (void) {
1072 static char fname[8192], *t;
1074 tokenWantFileName = 1;
1075 if (nextToken() != TK_ID) fatal("identifier expected");
1076 tokenWantFileName = 0;
1077 strcpy(fname, ictx->fname);
1078 #ifdef _WIN32
1079 for (t = fname; *t; ++t) if (*t == '\\') *t = '/';
1080 #endif
1081 t = strrchr(fname, '/');
1082 if (t != NULL) t[1] = 0; else fname[0] = 0;
1083 strcat(fname, tstr);
1084 if (incLevel > 64) fatal("too many includes");
1085 openFile(fname);
1086 nextToken();
1090 static void parseVarList (int type, int local, int gotext) {
1091 for (;;) {
1092 LabelInfo *l;
1094 if (nextToken() != TK_ID) fatal("identifier expected");
1095 if (tstr[0] == '.' && local <= 0) fatal("invalid variable name: '%s'", tstr);
1096 if (tstr[0] != '.' && local > 0) fatal("invalid variable name: '%s'", tstr);
1097 l = findLabel(tstr);
1098 if (l != NULL) {
1099 if (gotext) {
1100 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1101 if (l->type != type) fatal("can't change existing extern label type: '%s'", tstr);
1102 } else {
1103 fatal("duplicate variable or label: '%s'", tstr);
1106 if (l == NULL) {
1107 l = addLabel(tstr);
1108 l->type = type;
1109 if (local > 0 && vtloc <= vtlast) fatal("too many local vars");
1110 if (gotext) {
1111 l->value = 0;
1112 } else {
1113 l->value = (local>0)?(--vtloc):(type==LB_GVAR?vglast++:vtlast++);
1115 if (local < 0) l->ext = 1;
1117 if (gotext) l->ext = -1;
1118 if (l->value > 126) fatal("too many vars");
1119 if (nextToken() != ',') break;
1124 static void parsePublics (void) {
1125 for (;;) {
1126 LabelInfo *l;
1128 if (nextToken() != TK_ID) fatal("identifier expected");
1129 if (tstr[0] == '.') fatal("invalid label name: '%s'", tstr);
1130 l = findLabel(tstr);
1131 if (l != NULL) {
1132 l->ext = 1;
1133 } else {
1134 l = addLabel(tstr);
1135 l->ext = 1;
1136 l->type = LB_CODE;
1137 l->value = -1;
1139 if (nextToken() != ',') break;
1144 static void parseLocList (void) {
1145 for (;;) {
1146 LabelInfo *l;
1148 if (nextToken() != TK_ID) fatal("identifier expected");
1149 if (tstr[0] != '.') fatal("local identifier expected instead of '%s'", tstr);
1150 l = findLabel(tstr);
1151 if (l != NULL) fatal("can't redefine label as local: '%s'", tstr);
1152 l = addLabel(tstr);
1153 l->ext = 0;
1154 l->type = LB_SVAR;
1155 l->value = -1;
1156 if (nextToken() != '=') fatal("'=' expected");
1157 if (nextToken() != TK_NUM) fatal("number expected");
1158 l->value = tint;
1159 if (nextToken() != ',') break;
1164 static void parseConst (int ext, int gotext) {
1165 LabelInfo *l;
1167 if (nextToken() != TK_ID) fatal("identifier expected");
1168 if (tstr[0] == '.') fatal("invalid constant name: '%s'", tstr);
1169 l = findLabel(tstr);
1170 if (l != NULL) {
1171 if (gotext) {
1172 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1173 if (l->type != LB_CONST) fatal("can't change existing extern label type: '%s'", tstr);
1174 } else {
1175 fatal("constant must be unique: '%s'", tstr);
1178 if (l == NULL) {
1179 l = addLabel(tstr);
1180 l->type = LB_CONST;
1181 l->value = 0;
1183 if (!gotext) {
1184 if (nextToken() == '=') nextToken();
1185 if (token != TK_NUM) fatal("constant must be numeric");
1186 l->value = tint;
1188 if (ext) l->ext = 1;
1189 if (gotext) l->ext = -1;
1190 nextToken();
1194 static char procname[8192];
1195 static int proclocals = 0;
1196 static int procargs = 0;
1197 static int lastWasReturn = 0;
1200 static void parseProc (int ext) {
1201 LabelInfo *l, *a = NULL;
1202 int spt;
1204 lastWasReturn = 0;
1205 if (nextToken() != TK_ID) fatal("identifier expected");
1206 if (tstr[0] == '.') fatal("invalid proc name: '%s'", tstr);
1207 if (procname[0]) fatal("unclosed proc: '%s'", procname);
1208 strcpy(procname, tstr);
1209 proclocals = 0;
1210 procargs = 0;
1211 freeLocalLabels(0); // vars and code
1213 l = findLabel(tstr);
1214 if (l != NULL) {
1215 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate proc label: '%s'", tstr);
1216 fixupLabelRefs(l, pc);
1217 } else {
1218 l = addLabel(tstr);
1219 l->type = LB_CODE;
1220 l->value = pc;
1222 if (ext) l->ext = 1;
1224 nextToken();
1226 while (token == TK_LABELDEF && (strcmp(tstr, "arg") == 0 || strcmp(tstr, "args") == 0)) {
1227 for (;;) {
1228 if (nextToken() != TK_ID) fatal("identifier expected");
1229 if (tstr[0] != '.') fatal("argument name must starts with '.'");
1230 l = findLabel(tstr);
1231 if (l != NULL) fatal("duplicate argument: '%s'", l->name);
1232 l = addLabel(tstr);
1233 l->type = LB_SVAR;
1234 l->value = 666;
1235 ++procargs;
1236 if (nextToken() != ',') break;
1238 a = labels; //HACK!
1239 // fix values
1240 // -1: return address, -2: last arg
1241 for (spt = -2, l = a; l->type == LB_SVAR; l = l->next) {
1242 l->value = spt;
1243 --spt;
1247 spt = -1; // first local
1248 while (token == TK_LABELDEF && (strcmp(tstr, "local") == 0 || strcmp(tstr, "locals") == 0)) {
1249 for (;;) {
1250 if (nextToken() != TK_ID) fatal("identifier expected");
1251 if (tstr[0] != '.') fatal("local variable name must starts with '.'");
1252 l = findLabel(tstr);
1253 if (l != NULL) fatal("duplicate local: '%s'", l->name);
1254 l = addLabel(tstr);
1255 l->type = LB_SVAR;
1256 l->value = spt--;
1257 ++proclocals;
1258 // fix args
1259 for (l = a; l != NULL && l->type == LB_SVAR; l = l->next) --(l->value);
1260 if (nextToken() != ',') break;
1264 if (proclocals > 0) {
1265 // allocate stack
1266 OperandInfo op0;
1268 setIntOperand(&op0, -proclocals);
1269 emitInstruction(VM_POP, &op0, NULL, NULL);
1274 static void parseEndP (void) {
1275 if (!procname[0]) fatal("'endp' without 'proc'");
1276 if (nextToken() != TK_ID) fatal("identifier expected");
1277 if (strcmp(procname, tstr) != 0) fatal("endp for '%s' in proc '%s'", tstr, procname);
1278 //if (!lastWasReturn) fatal("no 'return' in proc");
1279 nextToken();
1280 procname[0] = 0;
1281 freeLocalLabels(0);
1285 static void doReturn (void) {
1286 OperandInfo op0, op1, op2;
1288 if (!procname[0]) fatal("'return' without 'proc'");
1289 lastWasReturn = 1;
1290 op0.var = op1.var = op2.var = -1;
1291 if (hasOperand()) getOperand(&op2, 0); // result
1292 setIntOperand(&op0, proclocals);
1293 setIntOperand(&op1, procargs);
1294 emitInstruction(VM_RET, &op0, &op1, &op2);
1298 static void parseLabel (int gotext) {
1299 LabelInfo *l;
1301 if (gotext && tstr[0] == '.') fatal("can't declare local extern label: '%s'", tstr);
1302 if (tstr[0] != '.' && tstr[0] != '@') {
1303 if (!gotext) {
1304 if (procname[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname);
1306 freeLocalLabels(0); // vars and code
1308 if (tstr[0] == '@') {
1309 char *d = tstr;
1311 while (*d++) d[-1] = d[0];
1312 d[-1] = 0;
1314 l = findLabel(tstr);
1315 if (l != NULL) {
1316 if (gotext) {
1317 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1318 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
1320 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate label: '%s'", tstr);
1321 fixupLabelRefs(l, pc);
1322 } else {
1323 l = addLabel(tstr);
1324 l->type = LB_CODE;
1325 l->value = pc;
1327 if (gotext) l->ext = -1;
1328 nextToken();
1332 static void parseDB (void) {
1333 for (;;) {
1334 nextToken();
1335 if (token == TK_ID) {
1336 LabelInfo *l = findLabel(tstr);
1338 if (l == NULL) fatal("unknown label: '%s'", tstr);
1339 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1340 l->used = 1;
1341 tint = l->value;
1342 addExternRef(l, pc, 1);
1343 } else if (token != TK_NUM) {
1344 fatal("number expected");
1346 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1347 emitByte(tint&0xff);
1348 if (nextToken() != ',') break;
1353 static void parseDW (void) {
1354 for (;;) {
1355 nextToken();
1356 if (token == TK_ID) {
1357 LabelInfo *l = findLabel(tstr);
1359 if (l == NULL) {
1360 l = addLabel(tstr);
1361 l->type = LB_CODE;
1362 l->value = -1;
1363 //fatal("unknown label: '%s'", tstr);
1365 addLabelRef(l, pc);
1366 addExternRef(l, pc, 2);
1367 tint = l->value;
1368 } else if (token != TK_NUM) {
1369 fatal("number expected");
1371 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1372 emitByte(tint&0xff);
1373 emitByte((tint>>8)&0xff);
1374 if (nextToken() != ',') break;
1379 // terminator eaten
1380 static void parseAndPutString (int qch) {
1381 for (;;) {
1382 int ch = nextChar();
1384 if (ch == EOF) fatal("unterminated string");
1385 if (qch == '"') {
1386 if (ch == qch) break;
1387 if (ch == '\\') {
1388 int n;
1390 ch = nextChar();
1391 if (ch == EOF) fatal("invalid escape");
1392 switch (ch) {
1393 case 'a': emitByte('\a'); break;
1394 case 'b': emitByte('\b'); break;
1395 case 'e': emitByte('\x1b'); break;
1396 case 'f': emitByte('\f'); break;
1397 case 'n': emitByte('\n'); break;
1398 case 'r': emitByte('\r'); break;
1399 case 't': emitByte('\t'); break;
1400 case 'v': emitByte('\v'); break;
1401 case '"': case '\'': case '\\': case ' ': emitByte(ch); break;
1402 case 'x':
1403 n = digit(nextChar(), 16);
1404 if (n < 0) fatal("invalid hex escape");
1405 ch = nextChar();
1406 if (ch == EOF) fatal("invalid hex escape");
1407 if (digit(ch, 16)) {
1408 n = n*16+digit(ch, 16);
1409 } else {
1410 ungetChar(ch);
1412 emitByte(ch);
1413 break;
1414 default: fatal("invalid escape: '%c'", ch);
1416 } else {
1417 emitByte(ch);
1419 } else {
1420 if (ch == qch) {
1421 ch = nextChar();
1422 if (ch == EOF) return;
1423 if (ch == qch) {
1424 emitByte(ch);
1425 continue;
1427 ungetChar(ch);
1428 break;
1429 } else {
1430 emitByte(ch);
1437 static void parseDAscii (int zeroend) {
1438 for (;;) {
1439 int ch;
1441 skipSpaces();
1442 ch = nextChar();
1443 if (ch == EOF) break;
1444 switch (ch) {
1445 case '"': tokenWantFileName = 1; parseAndPutString(ch); tokenWantFileName = 0; break;
1446 case '\'': tokenWantFileName = 1; parseAndPutString(ch); tokenWantFileName = 0; break;
1447 default:
1448 ungetChar(ch);
1449 nextToken();
1450 if (token == TK_ID) {
1451 LabelInfo *l = findLabel(tstr);
1453 if (l == NULL) fatal("unknown label: '%s'", tstr);
1454 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1455 l->used = 1;
1456 addExternRef(l, pc, 1);
1457 tint = l->value;
1458 } else if (token != TK_NUM) {
1459 fatal("number expected");
1461 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1462 emitByte(tint&0xff);
1464 if (nextToken() != ',') break;
1466 if (zeroend) emitByte(0);
1470 static void process (void) {
1471 int gotext = 0;
1472 LabelInfo *l = addLabel("retval");
1474 l->type = LB_TVAR;
1475 l->value = VM_VARS_SIZE-1;
1477 procname[0] = 0;
1478 lastWasReturn = 0;
1479 nextToken();
1480 while (token != TK_EOF) {
1481 int opc;
1483 if (token == TK_LABELDEF) {
1484 if (strcmp(tstr, "extern") == 0) {
1485 if (gotext) fatal("double 'extern'");
1486 gotext = 1;
1487 nextToken();
1488 continue;
1490 // new label or operator
1491 if (strcmp(tstr, "include") == 0) {
1492 parseInlcude();
1493 continue;
1494 } else if (strcmp(tstr, "defloc") == 0) {
1495 if (gotext) fatal("extern labels must not be locals");
1496 parseLocList();
1497 } else if (strcmp(tstr, "defgvar") == 0) {
1498 parseVarList(LB_GVAR, 0, gotext);
1499 } else if (strcmp(tstr, "defevar") == 0) { // extern global
1500 parseVarList(LB_GVAR, -1, gotext);
1501 } else if (strcmp(tstr, "deftvar") == 0) {
1502 freeLocalLabels(1); // only vars
1503 vtloc = VM_VARS_SIZE;
1504 parseVarList(LB_TVAR, 0, gotext);
1505 } else if (strcmp(tstr, "defetvar") == 0) {
1506 freeLocalLabels(1); // only vars
1507 vtloc = VM_VARS_SIZE;
1508 parseVarList(LB_TVAR, -1, gotext);
1509 } else if (strcmp(tstr, "deflvar") == 0) {
1510 if (gotext) fatal("extern labels must not be locals");
1511 parseVarList(LB_TVAR, 1, gotext);
1512 } else if (strcmp(tstr, "public") == 0) {
1513 if (gotext) fatal("invalid extern label declaration");
1514 parsePublics();
1515 } else if (strcmp(tstr, "proc") == 0 || strcmp(tstr, "eproc") == 0) {
1516 if (gotext) fatal("invalid extern label declaration");
1517 parseProc(tstr[0] == 'e');
1518 } else if (strcmp(tstr, "endp") == 0) {
1519 if (gotext) fatal("invalid extern label declaration");
1520 parseEndP();
1521 } else if (strcmp(tstr, "const") == 0 || strcmp(tstr, "econst") == 0) {
1522 parseConst(tstr[0] == 'e', gotext);
1523 } else if (strcmp(tstr, "db") == 0) {
1524 if (gotext) fatal("invalid extern label declaration");
1525 parseDB();
1526 } else if (strcmp(tstr, "dw") == 0) {
1527 if (gotext) fatal("invalid extern label declaration");
1528 parseDW();
1529 } else if (strcmp(tstr, "da") == 0) {
1530 if (gotext) fatal("invalid extern label declaration");
1531 parseDAscii(0);
1532 } else if (strcmp(tstr, "dz") == 0) {
1533 if (gotext) fatal("invalid extern label declaration");
1534 parseDAscii(1);
1535 } else {
1536 parseLabel(gotext);
1538 gotext = 0;
1539 continue;
1540 } else {
1541 if (gotext) {
1542 LabelInfo *l;
1544 if (token != TK_ID) fatal("label declaration expected after 'extern'");
1545 if (tstr[0] == '.') fatal("extern label can't be local: '%s'", tstr);
1546 l = findLabel(tstr);
1547 if (l != NULL) {
1548 if (l->ext >= 0) fatal("can't declare existing label as extern: '%s'", tstr);
1549 if (l->type != LB_CODE) fatal("can't change existing extern label type: '%s'", tstr);
1550 } else {
1551 l = addLabel(tstr);
1552 l->type = LB_CODE;
1553 l->value = 0;
1554 l->ext = -1;
1556 nextToken();
1557 continue;
1561 if (token < TK_VM_OPERATOR) fatal("mnemonics expected");
1562 opc = token;
1563 if (opc < 600) opc -= TK_VM_OPERATOR;
1564 nextToken();
1565 lastWasReturn = 0;
1566 switch (opc) {
1567 case VM_ADD:
1568 case VM_SUB:
1569 case VM_MUL:
1570 case VM_DIV:
1571 case VM_MOD:
1572 case VM_BOR:
1573 case VM_XOR:
1574 case VM_AND:
1575 doMath(opc);
1576 break;
1577 case VM_JEQ:
1578 case VM_JNE:
1579 case VM_JLT:
1580 case VM_JLE:
1581 case VM_JGT:
1582 case VM_JGE:
1583 doJXX(opc);
1584 break;
1585 case VM_JMP:
1586 doBranch(opc);
1587 break;
1588 case VM_END:
1589 doNoOperands(opc);
1590 break;
1591 case VM_BSR:
1592 doBSR(opc);
1593 break;
1594 case VM_NEW:
1595 doNew(opc);
1596 break;
1597 case VM_BRK:
1598 doNoOperands(opc);
1599 break;
1600 case VM_SET:
1601 doSet(opc);
1602 break;
1603 case VM_GET:
1604 doGet(opc);
1605 break;
1606 case VM_PSH:
1607 doPush(opc);
1608 break;
1609 case VM_POP:
1610 doPop(opc);
1611 break;
1612 case VM_SWP:
1613 doSwap(opc);
1614 break;
1615 case VM_PCK:
1616 case VM_ROL:
1617 doPickRoll(opc);
1618 break;
1619 case VM_DPT:
1620 doDepth(opc);
1621 break;
1622 case VM_TID:
1623 doTId(opc);
1624 break;
1625 case VM_KIL:
1626 case VM_SUS:
1627 case VM_RES:
1628 doKillSusRes(opc);
1629 break;
1630 case VM_STA:
1631 doSta(opc);
1632 break;
1633 case VM_RXC:
1634 doRXC(opc);
1635 break;
1636 case VM_WXC:
1637 doWXC(opc);
1638 break;
1639 case VM_RST:
1640 doRST(opc);
1641 break;
1642 case VM_MGF:
1643 case VM_MGB:
1644 doMap(opc, 1);
1645 break;
1646 case VM_MSF:
1647 case VM_MSB:
1648 doMap(opc, 0);
1649 break;
1650 case VM_RET:
1651 if (procname[0]) doReturn(); else doRet(opc);
1652 break;
1653 case VMX_DRP:
1654 doDrop(VM_POP);
1655 break;
1656 case VMX_DUP:
1657 doNoOperands(VM_PSH);
1658 break;
1659 case VMX_RTN:
1660 doRet(opc);
1661 break;
1662 case VMX_RETURN:
1663 doReturn();
1664 break;
1665 default:
1666 fatal("not yet");
1669 if (procname[0]) fatal("'proc' without 'endp': '%s'", procname);
1673 ////////////////////////////////////////////////////////////////////////////////
1674 static int cfWriteByte (FILE *fl, int value) {
1675 unsigned char b;
1677 b = (value&0xff)^SECRET;
1678 if (fwrite(&b, 1, 1, fl) != 1) return -1;
1679 return 0;
1683 static int cfWriteWord (FILE *fl, int value) {
1684 if (cfWriteByte(fl, value) != 0) return -1;
1685 if (cfWriteByte(fl, value>>8) != 0) return -1;
1686 return 0;
1690 static int cfWriteCode (FILE *fl) {
1691 for (int f = 0; f < pc; ++f) if (cfWriteByte(fl, vmcode[f]) != 0) return -1;
1692 return 0;
1696 static int cfWriteRels (FILE *fl) {
1697 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) {
1698 if (cfWriteWord(fl, r->pc) != 0) return -1;
1700 return 0;
1704 static int cfWritePublicLabels (FILE *fl) {
1705 for (LabelInfo *l = labels; l != NULL; l = l->next) {
1706 if (l->ext >= 0 && l->name[0]) {
1707 int len = strlen(l->name);
1709 if (cfWriteByte(fl, l->type|(l->ext>0?0x80:0)) != 0) return -1;
1711 switch (l->type) {
1712 case LB_GVAR:
1713 case LB_TVAR:
1714 if (cfWriteByte(fl, l->value) != 0) return -1;
1715 break;
1716 case LB_SVAR:
1717 case LB_CODE:
1718 case LB_CONST:
1719 if (cfWriteWord(fl, l->value) != 0) return -1;
1720 break;
1721 default:
1722 abort();
1725 if (len > 255) len = 255;
1726 if (cfWriteByte(fl, len) != 0) return -1;
1727 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
1730 return 0;
1734 static int cfWriteExtLabels (FILE *fl) {
1735 for (LabelInfo *l = labels; l != NULL; l = l->next) {
1736 if (l->ext < 0 && l->name[0] && l->refs != NULL) {
1737 int len = strlen(l->name), rcnt = 0;
1739 if (cfWriteByte(fl, l->type) != 0) return -1;
1740 if (len > 255) len = 255;
1741 if (cfWriteByte(fl, len) != 0) return -1;
1742 for (int f = 0; f < len; ++f) if (cfWriteByte(fl, (unsigned char)(l->name[f])) != 0) return -1;
1744 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) ++rcnt;
1745 if (cfWriteWord(fl, rcnt) != 0) return -1;
1746 for (LabelRefInfo *r = l->refs; r != NULL; r = r->next) {
1747 if (cfWriteByte(fl, r->size) != 0) return -1;
1748 if (cfWriteWord(fl, r->pc) != 0) return -1;
1752 return 0;
1756 static int writeCodeFile (FILE *fl) {
1757 static const char *sign = "AVM1";
1758 int lcnt = 0, elcnt = 0;
1759 int rcnt = 0;
1761 relrefs = lrefRemoveDups(relrefs);
1762 for (LabelRefInfo *r = relrefs; r != NULL; r = r->next) ++rcnt;
1763 for (LabelInfo *l = labels; l != NULL; l = l->next) {
1764 if (l->name[0]) {
1765 if (l->ext >= 0) ++lcnt;
1766 if (l->ext < 0) {
1767 l->refs = lrefRemoveDups(l->refs);
1768 if (l->refs != NULL) ++elcnt;
1772 fprintf(stderr, "%d bytes of code, %d public labels, %d fixups, %d externs\n", pc, lcnt, rcnt, elcnt);
1774 if (fwrite(sign, 4, 1, fl) != 1) return -1;
1775 // code size
1776 if (cfWriteWord(fl, pc) != 0) return -1;
1777 // number of fixups
1778 if (cfWriteWord(fl, rcnt) != 0) return -1;
1779 // number of extern labels
1780 if (cfWriteWord(fl, elcnt) != 0) return -1;
1781 // number of labels
1782 if (cfWriteWord(fl, lcnt) != 0) return -1;
1784 if (cfWriteCode(fl) != 0) return -1;
1785 if (cfWriteRels(fl) != 0) return -1;
1786 if (cfWriteExtLabels(fl) != 0) return -1;
1787 if (cfWritePublicLabels(fl) != 0) return -1;
1789 return 0;
1793 ////////////////////////////////////////////////////////////////////////////////
1794 #ifdef _WIN32
1795 # include "cmdline.c"
1796 #endif
1799 ////////////////////////////////////////////////////////////////////////////////
1800 int main (int argc, char *argv[]) {
1801 #ifdef _WIN32
1802 cmdLineParse();
1803 argc = k8argc;
1804 argv = k8argv;
1805 #endif
1806 if (argc != 3) {
1807 fprintf(stderr, "usage: awasm infile outfile\n");
1808 return 1;
1810 openFile(argv[1]);
1811 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
1812 process();
1813 while (ictx != NULL) closeFile();
1814 freeLocalLabels(0); // vars and code
1815 checkLabels();
1816 //if (argc > 3) dumpGlobalVars(argv[3]);
1818 FILE *fo = fopen(argv[2], "wb");
1819 int res;
1821 if (fo == NULL) { fprintf(stderr, "FATAL: can't create output file: '%s'\n", argv[2]); return 1; }
1822 res = writeCodeFile(fo);
1823 if (fclose(fo) != 0) res = -1;
1824 if (res != 0) {
1825 fprintf(stderr, "FATAL: error writing output file: '%s'\n", argv[2]);
1826 unlink(argv[2]);
1827 return 1;
1830 freeLabels();
1831 freeLabelRefList(relrefs);
1832 return 0;