awasm, assembler: report unused labels
[awish.git] / src / awasm.c
blob817b8c333b5febf92c0d65f3842e221280fe80ce
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 enum {
28 LB_GVAR,
29 LB_TVAR,
30 LB_SVAR,
31 LB_CODE,
32 LB_CONST
36 static const char *ltnames[] = {
37 "GVAR",
38 "TVAR",
39 "SVAR",
40 "CODE",
41 "CONST"
45 ////////////////////////////////////////////////////////////////////////////////
46 #define MAX_TOKEN_LENGTH (256)
48 enum {
49 TK_EOF = -1,
50 TK_ID = 256,
51 TK_NUM,
52 TK_LABELDEF,
54 TK_VM_OPERATOR = 400
58 enum {
59 VMX_DRP = 600,
60 VMX_DUP,
61 VMX_RTN,
62 VMX_RETURN
66 ////////////////////////////////////////////////////////////////////////////////
67 static uint8_t vmcode[65536];
68 static int pc = 0;
71 ////////////////////////////////////////////////////////////////////////////////
72 typedef struct InputContext {
73 struct InputContext *prev;
74 FILE *fl;
75 char *fname;
76 int lineno;
77 int ucnt;
78 int uca[4];
79 } InputContext;
82 static InputContext *ictx = NULL;
83 static int token;
84 static char tstr[MAX_TOKEN_LENGTH+1];
85 static int tint;
86 static int incLevel = 0;
89 ////////////////////////////////////////////////////////////////////////////////
90 static __attribute__((__noreturn__)) __attribute__((format(printf, 1, 2))) void fatal (const char *fmt, ...) {
91 va_list ap;
93 if (ictx != NULL) {
94 fprintf(stderr, "FATAL (line %d, file '%s'): ", ictx->lineno, ictx->fname);
95 } else {
96 fprintf(stderr, "FATAL: ");
98 va_start(ap, fmt);
99 vfprintf(stderr, fmt, ap);
100 va_end(ap);
101 fprintf(stderr, "\n");
102 exit(1);
106 ////////////////////////////////////////////////////////////////////////////////
107 static void openFile (const char *fname) {
108 InputContext *ic = malloc(sizeof(InputContext));
110 if (ic == NULL) abort();
111 ic->fl = fopen(fname, "r");
112 if (ic->fl == NULL) fatal("can't open file: '%s'", fname);
113 ic->prev = ictx;
114 ic->fname = strdup(fname);
115 #ifdef _WIN32
116 for (char *t = ic->fname; *t; ++t) if (*t == '\\') *t = '/';
117 #endif
118 ic->lineno = 1;
119 ic->ucnt = 0;
120 ictx = ic;
121 for (int f = 0; f < incLevel; ++f) fputc(' ', stderr);
122 ++incLevel;
123 fprintf(stderr, "compiling: %s\n", ic->fname);
127 static void closeFile (void) {
128 if (ictx != NULL) {
129 InputContext *ic = ictx;
131 ictx = ic->prev;
132 fclose(ic->fl);
133 if (ic->fname) free(ic->fname);
134 free(ic);
135 --incLevel;
137 if (ictx != NULL) {
138 for (int f = 0; f < incLevel-1; ++f) fputc(' ', stderr);
139 fprintf(stderr, "compiling: %s\n", ictx->fname);
146 ////////////////////////////////////////////////////////////////////////////////
147 static void ungetChar (int c) {
148 if (c != EOF && ictx != NULL) {
149 if (ictx->ucnt >= 4) fatal("too many unread chars");
150 ictx->uca[ictx->ucnt++] = c;
155 static int tokenWantFileName = 0;
157 static int nextChar (void) {
158 int c;
160 if (ictx == NULL) return EOF;
161 if (ictx->ucnt > 0) {
162 c = ictx->uca[--ictx->ucnt];
163 } else {
164 c = fgetc(ictx->fl);
165 if (c == EOF) {
166 closeFile();
167 c = (ictx==NULL ? EOF : '\n');
168 } else {
169 if (c == '\n') ++(ictx->lineno);
172 if (!tokenWantFileName) {
173 if (c >= 'A' && c <= 'Z') c += 32; // tolower
175 return c;
179 static void skipSpaces (void) {
180 for (;;) {
181 int c = nextChar();
183 if (c == EOF) return;
184 if (c == ';') {
185 do { c = nextChar(); } while (c != EOF && c != '\n');
186 continue;
188 if (c == '/') {
189 int c1 = nextChar();
191 if (c1 == '/') {
192 do { c = nextChar(); } while (c != EOF && c != '\n');
193 continue;
195 if (c1 == '*') {
196 for (;;) {
197 c = nextChar();
198 if (c == EOF) break;
199 if (c == '*') {
200 c = nextChar();
201 if (c == '/') break;
204 continue;
206 ungetChar(c1);
207 ungetChar(c);
208 break;
210 if (c > 32) { ungetChar(c); break; }
215 static int digit (int c, int base) {
216 if (c == EOF) return -1;
217 if (c >= 'a' && c <= 'z') c -= 32;
218 if (c > '9' && c < 'A') return -1;
219 if (c > '9') c -= 7;
220 c -= '0';
221 if (c < 0 || c >= base) return -1;
222 return c;
226 static void getNumber (int c) {
227 int base = 10;
229 token = TK_NUM;
230 tint = 0;
231 if (c == '0') {
232 c = nextChar();
233 if (c == EOF) return;
234 if (!isdigit(c)) {
235 switch (c) {
236 case 'b': case 'B': base = 2; break;
237 case 'o': case 'O': base = 8; break;
238 case 'd': case 'D': base = 10; break;
239 case 'x': case 'X': base = 16; break;
240 default:
241 if (isalpha(c)) fatal("invalid number");
242 if (c != EOF) ungetChar(c);
243 return;
245 c = nextChar();
246 if (digit(c, base) < 0) fatal("invalid number");
250 for (;;) {
251 int d = digit(c, base);
253 if (d < 0) break;
254 tint = (tint*base)+d;
255 if (tint > 32767) fatal("number constant too big");
256 c = nextChar();
258 if (c != EOF && isalpha(c)) fatal("invalid number");
259 if (c != EOF) ungetChar(c);
263 static int nextToken (void) {
264 int c;
266 memset(tstr, 0, sizeof(tstr));
267 tint = 0;
268 token = TK_EOF;
269 skipSpaces();
270 if (ictx == NULL) return TK_EOF;
271 c = nextChar();
272 if (c == EOF) return TK_EOF;
273 if (c >= 127) fatal("invalid char (%d)", c);
274 tstr[0] = c;
275 tstr[1] = 0;
277 if (isalpha(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
278 // id or label
279 int f = 1;
281 for (;;) {
282 c = nextChar();
283 if (isalnum(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && (c == '/' || c == '\\'))) {
284 if (f >= MAX_TOKEN_LENGTH-1) fatal("identifier too long");
285 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
286 tstr[f++] = c;
287 } else {
288 //if (c != ';' && c > 32) fatal("invalid identifier");
289 break;
292 tstr[f] = 0;
293 if (!isalnum(tstr[0]) && !tstr[1]) {
294 if (c != EOF && c > 32) ungetChar(c);
295 token = tstr[0];
296 return token;
298 token = TK_ID;
299 // label definition?
300 if (!tokenWantFileName) {
301 if (c == ':') {
302 token = TK_LABELDEF;
303 } else if (c <= 32 && c != '\n') {
304 for (;;) {
305 c = nextChar();
306 if (c == ':') { token = TK_LABELDEF; break; }
307 if (c > 32) ungetChar(c);
308 if (c == EOF || c > 32) break;
310 } else {
311 if (c != EOF && c > 32) ungetChar(c);
313 // vm mnemonics?
314 if (f == 3) {
315 for (f = 0; vmOpNames[f]; ++f) {
316 if (strcmp(tstr, vmOpNames[f]) == 0) {
317 if (token == TK_LABELDEF) fatal("invalid label: '%s'", tstr);
318 token = TK_VM_OPERATOR+f;
319 break;
322 if (token < TK_VM_OPERATOR) {
323 if (strcmp(tstr, "drp") == 0) token = VMX_DRP;
324 else if (strcmp(tstr, "dup") == 0) token = VMX_DUP;
325 else if (strcmp(tstr, "rtn") == 0) token = VMX_RTN;
327 } else if (strcmp(tstr, "return") == 0) {
328 token = VMX_RETURN;
331 } else if (c == '-' || c == '+') {
332 int neg = (c=='-');
334 token = c;
335 if ((c = nextChar()) != EOF) {
336 if (isdigit(c)) {
337 getNumber(c);
338 if (neg) tint = -tint;
339 } else {
340 ungetChar(c);
343 } else if (isdigit(c)) {
344 // number
345 getNumber(c);
346 } else {
347 // delimiter
348 token = c;
350 return token;
354 ////////////////////////////////////////////////////////////////////////////////
355 typedef struct LabelInfo {
356 struct LabelInfo *next;
357 char *name;
358 int type; // LB_XXXX
359 int value; // code && <0: not defined yet (forward ref)
360 int ext; // extern?
361 int fixCount;
362 int *fixes; // 65536
363 int refCount;
364 int *refs; // 65536
365 int used;
366 } LabelInfo;
369 static LabelInfo *labels = NULL;
371 static int vglast = 0;
372 static int vtlast = 0;
373 static int vtloc = VM_VARS_SIZE-1; // local thread (var 126 is used for internal thing)
376 static void addLabelRef (LabelInfo *l, int pc) {
377 if (l != NULL && l->type == LB_CODE && l->value < 0) {
378 if (l->fixes == NULL) {
379 l->fixes = malloc(sizeof(int)*32768);
380 if (l->fixes == NULL) fatal("out of memory");
381 l->fixCount = 0;
383 if (l->fixCount > 32760) {
384 fatal("too many label references");
385 } else {
386 l->fixes[l->fixCount++] = pc;
388 //fprintf(stderr, "*[%s] fixup #%d at 0x%04x\n", l->name, l->fixCount-1, pc);
393 static void fixupLabelRefs (LabelInfo *l, int pc) {
394 if (l != NULL) {
395 // code fixup
396 if (l->fixCount > 0) l->used = 1;
397 l->value = pc;
398 for (int f = 0; f < l->fixCount; ++f) {
399 //fprintf(stderr, "[%s] FIXUP #%d: 0x%04x=0x%04x\n", l->name, f, l->fixes[f], pc);
400 vmcode[l->fixes[f]] = pc&0xff;
401 vmcode[l->fixes[f]+1] = (pc>>8)&0xff;
403 l->fixCount = 0;
408 static void checkLabels (void) {
409 for (LabelInfo *l = labels; l != NULL; l = l->next) {
410 if (l->type == LB_CODE && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
411 if (!l->used) { l->used = -1; fprintf(stderr, "WARNING: unused %s label '%s'\n", ltnames[l->type], l->name); }
416 static void freeLabels (void) {
417 checkLabels();
418 while (labels) {
419 LabelInfo *l = labels;
421 labels = l->next;
422 if (l->fixes) free(l->fixes);
423 free(l->name);
424 free(l);
429 static void freeLocalLabels (int onlyVars) {
430 LabelInfo *p = NULL, *l = labels;
432 while (l != NULL) {
433 LabelInfo *n = l->next;
435 if ((!onlyVars || l->type != LB_CODE) && l->name[0] == '.') {
436 if (l->type == LB_CODE && l->value < 0) fatal("undefined %slabel: '%s'", l->used?"":"not referenced ", l->name);
437 if (!l->used) { l->used = -1; fprintf(stderr, "WARNING: unused %s label '%s'\n", ltnames[l->type], l->name); }
438 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
439 if (l->fixes) free(l->fixes);
440 free(l->name);
441 free(l);
442 if (p != NULL) p->next = n; else labels = n;
443 } else {
444 p = l;
446 l = n;
451 static LabelInfo *findLabel (const char *name) {
452 if (name && name[0] == '@') ++name;
453 if (!name || !name[0]) return NULL;
454 for (LabelInfo *l = labels; l != NULL; l = l->next) if (strcmp(l->name, name) == 0) return l;
455 return NULL;
459 static LabelInfo *addLabel (const char *name) {
460 LabelInfo *l;
462 if (!name || !name[0]) abort();
463 //if (strcmp(name, "item_glovesg") == 0) fatal("!!!");
464 if (findLabel(name)) fatal("duplicate label: '%s'", name);
465 l = malloc(sizeof(LabelInfo));
466 if (l == NULL) fatal("out of memory");
467 l->name = strdup(name);
468 if (l->name == NULL) fatal("out of memory");
469 l->type = LB_CODE;
470 l->value = -1;
471 l->ext = 0;
472 l->fixCount = 0;
473 l->fixes = NULL;
474 l->next = labels;
475 labels = l;
476 return l;
480 ////////////////////////////////////////////////////////////////////////////////
481 typedef struct {
482 LabelInfo *l; // !=NULL: label
483 int var; // [...]
484 int value; // if l==NULL
485 int vartype; // 0: global; 1: tlocal; 2: stack
486 } OperandInfo;
489 static void setIntOperand (OperandInfo *op, int value) {
490 op->l = NULL;
491 op->var = 0;
492 op->value = value;
493 op->vartype = -1;
497 static void setLabelOperand (OperandInfo *op, LabelInfo *l) {
498 op->l = l;
499 op->var = 0;
500 op->value = 0;
501 op->vartype = -1;
505 static void getOperand (OperandInfo *op, int wantCode) {
506 if (token == TK_ID) {
507 LabelInfo *l = findLabel(tstr);
509 if (l == NULL) {
510 // new code label
511 l = addLabel(tstr);
512 l->type = LB_CODE;
513 l->value = -1;
514 } else {
515 if (wantCode && l->type != LB_CODE) fatal("code offset expected");
517 setLabelOperand(op, l);
518 nextToken();
519 return;
522 if (token == TK_NUM) {
523 if (wantCode) fatal("code offset expected");
524 setIntOperand(op, tint);
525 nextToken();
526 return;
529 if (token == '[') {
530 //if (wantCode) fatal("code offset expected");
531 nextToken();
532 if (token == TK_ID) {
533 LabelInfo *l = findLabel(tstr);
535 if (l == NULL || l->type == LB_CODE) fatal("unknown variable: '%s'", tstr);
536 setLabelOperand(op, l);
537 } else if (token == '@' || token == '.') {
538 int loc = token;
540 if (nextToken() != TK_NUM) fatal("index expected");
541 setIntOperand(op, tint);
542 if (loc != '.') {
543 // not stack
544 if (tint < 0 || tint > 126) fatal("invalid variable index");
545 op->vartype = loc=='@' ? 0 : 1;
546 } else {
547 // stack
548 op->vartype = 2;
550 } else if (token == TK_NUM) {
551 // local
552 if (tint < 0 || tint > 126) fatal("invalid variable index");
553 setIntOperand(op, tint);
554 op->vartype = 1;
555 } else {
556 fatal("invalid operand");
558 op->var = 1;
559 if (nextToken() != ']') fatal("']' expected");
560 nextToken();
561 return;
564 fprintf(stderr, "*%d [%s] %d\n", token, tstr, tint);
565 fatal("invalid operand");
569 static inline int hasOperand (void) {
570 return (token != TK_EOF && token != TK_LABELDEF && token < TK_VM_OPERATOR);
574 ////////////////////////////////////////////////////////////////////////////////
575 static void emitByte (int b) {
576 if (b < 0 || b > 255) fatal("internal error");
577 if (pc >= 32768) fatal("code too big");
578 vmcode[pc++] = b;
582 static void emitOpCode (int opc, int opcount) {
583 if (opc < 0 || opc > 63 || opcount < 0 || opcount > 3) fatal("internal error");
584 emitByte(opc|(opcount<<6));
589 static void emitInteger (int val) {
590 emitByte(255); // special
591 emitByte(val&0xff);
592 emitByte((val>>8)&0xff);
597 static void emitOperand (OperandInfo *op) {
598 if (!op || op->var < 0) return;
600 if (op->var > 0) {
601 // variable
602 if (op->l) {
603 // from label
604 op->l->used = 1;
605 switch (op->l->type) {
606 case LB_GVAR: emitByte(op->l->value|0x80); break;
607 case LB_TVAR: emitByte(op->l->value); break;
608 case LB_SVAR:
609 emitByte(127); // special
610 emitByte(op->l->value&0xff);
611 emitByte((op->l->value>>8)&0xff);
612 break;
613 default: fatal("internal error");
615 } else {
616 // from value
617 switch (op->vartype) {
618 case 0: emitByte(op->value|0x80); break; // global
619 case 1: emitByte(op->value); break; // tlocal
620 case 2: // stack
621 emitByte(127); // special
622 emitByte(op->value&0xff);
623 emitByte((op->value>>8)&0xff);
624 break;
625 default: fatal("internal error");
628 return;
631 if (op->var == 0) {
632 // immediate
633 emitByte(255); // special
634 if (op->l) {
635 // from label
636 op->l->used = 1;
637 addLabelRef(op->l, pc);
638 emitByte(op->l->value&0xff);
639 emitByte((op->l->value>>8)&0xff);
640 } else {
641 // direct
642 if (op->value < -32767 || op->value > 32767) fatal("invalid value");
643 emitByte(op->value&0xff);
644 emitByte((op->value>>8)&0xff);
650 static void emitInstruction (int opc, OperandInfo *op0, OperandInfo *op1, OperandInfo *op2) {
651 int ocnt = 0;
653 if (op0 && op0->var >= 0) ++ocnt;
654 if (ocnt == 1 && op1 && op1->var >= 0) ++ocnt;
655 if (ocnt == 2 && op2 && op2->var >= 0) ++ocnt;
656 emitOpCode(opc, ocnt);
657 if (ocnt > 0) emitOperand(op0);
658 if (ocnt > 1) emitOperand(op1);
659 if (ocnt > 2) emitOperand(op2);
663 ////////////////////////////////////////////////////////////////////////////////
664 static void doNoOperands (int opcode) {
665 emitInstruction(opcode, NULL, NULL, NULL);
669 static void doMath (int opcode) {
670 OperandInfo op0, op1, op2;
672 op0.var = op1.var = op2.var = -1;
673 if (hasOperand()) {
674 getOperand(&op0, 0);
675 if (token == ',') {
676 nextToken();
677 getOperand(&op1, 0);
678 if (token == ',') {
679 nextToken();
680 getOperand(&op2, 0);
681 if (op2.var != 1) fatal("variable expected as third operand");
682 } else {
683 if (op0.var != 1) fatal("variable expected as first operand");
687 emitInstruction(opcode, &op0, &op1, &op2);
691 static void doJXX (int opcode) {
692 OperandInfo op0, op1, op2;
694 op0.var = op1.var = op2.var = -1;
695 if (hasOperand()) {
696 getOperand(&op0, 1);
697 if (token == ',') {
698 nextToken();
699 getOperand(&op1, 0);
700 if (token == ',') {
701 nextToken();
702 getOperand(&op2, 0);
706 emitInstruction(opcode, &op0, &op1, &op2);
710 static void doBranch (int opcode) {
711 OperandInfo op0;
713 op0.var = -1;
714 if (hasOperand()) {
715 getOperand(&op0, 1);
717 emitInstruction(opcode, &op0, NULL, NULL);
721 static void doBSR (int opcode) {
722 OperandInfo op0, op1, op2;
724 op0.var = op1.var = op2.var = -1;
725 if (hasOperand()) {
726 getOperand(&op0, 1);
727 if (token == ',') {
728 nextToken();
729 getOperand(&op1, 0);
730 if (token == ',') {
731 nextToken();
732 getOperand(&op2, 0);
736 emitInstruction(opcode, &op0, &op1, &op2);
740 static void doMap (int opcode, int lastMBV) {
741 OperandInfo op0, op1, op2;
743 op0.var = op1.var = op2.var = -1;
744 if (hasOperand()) {
745 getOperand(&op0, 0);
746 if (token == ',') {
747 nextToken();
748 getOperand(&op1, 0);
749 if (token == ',') {
750 nextToken();
751 getOperand(&op2, 0);
755 emitInstruction(opcode, &op0, &op1, &op2);
759 static void doRST (int opcode) {
760 OperandInfo op0, op1, op2;
762 op0.var = op1.var = op2.var = -1;
763 if (hasOperand()) {
764 getOperand(&op0, 0);
765 if (token == ',') {
766 nextToken();
767 getOperand(&op1, 0);
768 if (token == ',') {
769 nextToken();
770 getOperand(&op2, 0);
774 emitInstruction(opcode, &op0, &op1, &op2);
778 static void doSet (int opcode) {
779 OperandInfo op0, op1, op2;
781 op0.var = op1.var = op2.var = -1;
782 getOperand(&op0, 0);
783 if (token == ',') {
784 nextToken();
785 getOperand(&op1, 0);
786 if (token == ',') {
787 nextToken();
788 getOperand(&op2, 0);
791 if (op2.var < 0 && op0.var != 1) fatal("first operand should be var");
792 emitInstruction(opcode, &op0, &op1, &op2);
796 static void doDrop (int opcode) {
797 OperandInfo op0;
799 op0.var = -1;
800 if (hasOperand()) getOperand(&op0, 0);
801 if (op0.var > 0) fatal("number expected");
802 if (op0.value < 0) fatal("positive number expected");
803 emitInstruction(opcode, &op0, NULL, NULL);
807 static void doPush (int opcode) {
808 OperandInfo op0, op1, op2;
810 op0.var = op1.var = op2.var = -1;
811 getOperand(&op0, 0);
812 if (token == ',') {
813 nextToken();
814 getOperand(&op1, 0);
815 if (token == ',') {
816 nextToken();
817 getOperand(&op2, 0);
820 emitInstruction(opcode, &op0, &op1, &op2);
824 static void doPop (int opcode) {
825 OperandInfo op0, op1, op2;
827 op0.var = op1.var = op2.var = -1;
828 if (hasOperand()) {
829 getOperand(&op0, 0);
830 if (token == ',') {
831 nextToken();
832 getOperand(&op1, 0);
833 if (token == ',') {
834 nextToken();
835 getOperand(&op2, 0);
839 emitInstruction(opcode, &op0, &op1, &op2);
843 static void doSwap (int opcode) {
844 OperandInfo op0, op1;
846 op0.var = op1.var = -1;
847 if (hasOperand()) {
848 getOperand(&op0, 0);
849 if (token == ',') {
850 nextToken();
851 getOperand(&op1, 0);
854 emitInstruction(opcode, &op0, &op1, NULL);
858 static void doDepth (int opcode) {
859 OperandInfo op0;
861 op0.var = -1;
862 if (hasOperand()) getOperand(&op0, 0);
863 emitInstruction(opcode, &op0, NULL, NULL);
867 static void doPickRoll (int opcode) {
868 OperandInfo op0, op1;
870 op0.var = op1.var = -1;
871 if (hasOperand()) {
872 getOperand(&op0, 0);
873 if (token == ',') {
874 nextToken();
875 getOperand(&op1, 0);
876 if (op1.var != 1) fatal("second argument must be variable");
879 emitInstruction(opcode, &op0, &op1, NULL);
883 static void doNew (int opcode) {
884 OperandInfo op0, op1;
886 op0.var = op1.var = -1;
887 if (hasOperand()) {
888 getOperand(&op0, 0);
889 if (token == ',') {
890 nextToken();
891 getOperand(&op1, 0);
894 emitInstruction(opcode, &op0, &op1, NULL);
898 static void doTId (int opcode) {
899 OperandInfo op0;
901 op0.var = -1;
902 if (hasOperand()) getOperand(&op0, 0);
903 emitInstruction(opcode, &op0, NULL, NULL);
907 static void doKillSusRes (int opcode) {
908 OperandInfo op0;
910 op0.var = -1;
911 if (hasOperand()) getOperand(&op0, 0);
912 emitInstruction(opcode, &op0, NULL, NULL);
916 static void doSta (int opcode) {
917 OperandInfo op0, op1;
919 op0.var = op1.var = -1;
920 if (hasOperand()) {
921 getOperand(&op0, 0);
922 if (token == ',') {
923 nextToken();
924 getOperand(&op1, 0);
925 if (op1.var != 1) fatal("variable expected");
928 emitInstruction(opcode, &op0, &op1, NULL);
932 static void doGet (int opcode) {
933 OperandInfo op0, op1, op2;
935 op0.var = op1.var = op2.var = -1;
936 getOperand(&op0, 0);
937 if (token != ',') fatal("at least two operands expected");
938 nextToken();
939 getOperand(&op1, 0);
940 if (token == ',') {
941 nextToken();
942 getOperand(&op2, 0);
943 if (op2.var != 1) fatal("variable expected as third operand");
945 emitInstruction(opcode, &op0, &op1, &op2);
949 static void doRXC (int opcode) {
950 OperandInfo op0, op1;
952 op0.var = op1.var = -1;
953 if (hasOperand()) {
954 getOperand(&op0, 0);
955 if (token == ',') {
956 nextToken();
957 getOperand(&op1, 0);
958 if (op1.var != 1) fatal("variable expected as second operand");
961 emitInstruction(opcode, &op0, &op1, NULL);
965 static void doWXC (int opcode) {
966 OperandInfo op0, op1;
968 op0.var = op1.var = -1;
969 if (hasOperand()) {
970 getOperand(&op0, 0);
971 if (token == ',') {
972 nextToken();
973 getOperand(&op1, 0);
976 emitInstruction(opcode, &op0, &op1, NULL);
980 static void doRet (int opcode) {
981 OperandInfo op0, op1, op2;
983 op0.var = op1.var = op2.var = -1;
984 if (hasOperand()) {
985 getOperand(&op0, 0);
986 if (token != ',') fatal("at least two operands expected");
987 nextToken();
988 getOperand(&op1, 0);
989 if (token == ',') {
990 nextToken();
991 getOperand(&op2, 0);
994 emitInstruction(opcode, &op0, &op1, &op2);
998 ////////////////////////////////////////////////////////////////////////////////
999 static void parseInlcude (void) {
1000 static char fname[8192], *t;
1002 tokenWantFileName = 1;
1003 if (nextToken() != TK_ID) fatal("identifier expected");
1004 tokenWantFileName = 0;
1005 strcpy(fname, ictx->fname);
1006 #ifdef _WIN32
1007 for (t = fname; *t; ++t) if (*t == '\\') *t = '/';
1008 #endif
1009 t = strrchr(fname, '/');
1010 if (t != NULL) t[1] = 0; else fname[0] = 0;
1011 strcat(fname, tstr);
1012 if (incLevel > 64) fatal("too many includes");
1013 openFile(fname);
1014 nextToken();
1018 static void parseVarList (int type, int local) {
1019 for (;;) {
1020 LabelInfo *l;
1022 if (nextToken() != TK_ID) fatal("identifier expected");
1023 if (tstr[0] == '.' && local <= 0) fatal("invalid variable name: '%s'", tstr);
1024 if (tstr[0] != '.' && local > 0) fatal("invalid variable name: '%s'", tstr);
1025 l = findLabel(tstr);
1026 if (l != NULL) fatal("duplicate variable or label: '%s'", tstr);
1027 l = addLabel(tstr);
1028 l->type = type;
1029 if (local > 0 && vtloc <= vtlast) fatal("too many local vars");
1030 l->value = local>0?(--vtloc):(type==LB_GVAR?vglast++:vtlast++);
1031 if (local < 0) l->ext = 1;
1032 if (l->value > 126) fatal("too many vars");
1033 if (nextToken() != ',') break;
1038 static void parseExterns (void) {
1039 for (;;) {
1040 LabelInfo *l;
1042 if (nextToken() != TK_ID) fatal("identifier expected");
1043 if (tstr[0] == '.') fatal("invalid label name: '%s'", tstr);
1044 l = findLabel(tstr);
1045 if (l != NULL) {
1046 l->ext = 1;
1047 } else {
1048 l = addLabel(tstr);
1049 l->ext = 1;
1050 l->type = LB_CODE;
1051 l->value = -1;
1053 if (nextToken() != ',') break;
1058 static void parseLocList (void) {
1059 for (;;) {
1060 LabelInfo *l;
1062 if (nextToken() != TK_ID) fatal("identifier expected");
1063 if (tstr[0] != '.') fatal("local identifier expected instead of '%s'", tstr);
1064 l = findLabel(tstr);
1065 if (l != NULL) fatal("can't redefine label as local: '%s'", tstr);
1066 l = addLabel(tstr);
1067 l->ext = 0;
1068 l->type = LB_SVAR;
1069 l->value = -1;
1070 if (nextToken() != '=') fatal("'=' expected");
1071 if (nextToken() != TK_NUM) fatal("number expected");
1072 l->value = tint;
1073 if (nextToken() != ',') break;
1078 static void parseConst (int ext) {
1079 LabelInfo *l;
1081 if (nextToken() != TK_ID) fatal("identifier expected");
1082 if (tstr[0] == '.') fatal("invalid constant name: '%s'", tstr);
1083 l = findLabel(tstr);
1084 if (l != NULL) fatal("constant must be unique: '%s'", tstr);
1085 l = addLabel(tstr);
1086 if (nextToken() == '=') nextToken();
1087 if (token != TK_NUM) fatal("constant must be numeric");
1088 l->type = LB_CONST;
1089 l->value = tint;
1090 l->ext = ext;
1091 nextToken();
1095 static char procname[8192];
1096 static int proclocals = 0;
1097 static int procargs = 0;
1098 static int lastWasReturn = 0;
1101 static void parseProc (int ext) {
1102 LabelInfo *l, *a = NULL;
1103 int spt;
1105 lastWasReturn = 0;
1106 if (nextToken() != TK_ID) fatal("identifier expected");
1107 if (tstr[0] == '.') fatal("invalid proc name: '%s'", tstr);
1108 if (procname[0]) fatal("unclosed proc: '%s'", procname);
1109 strcpy(procname, tstr);
1110 proclocals = 0;
1111 procargs = 0;
1112 freeLocalLabels(0); // vars and code
1114 l = findLabel(tstr);
1115 if (l != NULL) {
1116 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate proc label: '%s'", tstr);
1117 fixupLabelRefs(l, pc);
1118 } else {
1119 l = addLabel(tstr);
1120 l->type = LB_CODE;
1121 l->value = pc;
1123 if (ext) l->ext = 1;
1125 nextToken();
1127 while (token == TK_LABELDEF && (strcmp(tstr, "arg") == 0 || strcmp(tstr, "args") == 0)) {
1128 for (;;) {
1129 if (nextToken() != TK_ID) fatal("identifier expected");
1130 if (tstr[0] != '.') fatal("argument name must starts with '.'");
1131 l = findLabel(tstr);
1132 if (l != NULL) fatal("duplicate argument: '%s'", l->name);
1133 l = addLabel(tstr);
1134 l->type = LB_SVAR;
1135 l->value = 666;
1136 ++procargs;
1137 if (nextToken() != ',') break;
1139 a = labels; //HACK!
1140 // fix values
1141 // -1: return address, -2: last arg
1142 for (spt = -2, l = a; l->type == LB_SVAR; l = l->next) {
1143 l->value = spt;
1144 --spt;
1148 spt = -1; // first local
1149 while (token == TK_LABELDEF && (strcmp(tstr, "local") == 0 || strcmp(tstr, "locals") == 0)) {
1150 for (;;) {
1151 if (nextToken() != TK_ID) fatal("identifier expected");
1152 if (tstr[0] != '.') fatal("local variable name must starts with '.'");
1153 l = findLabel(tstr);
1154 if (l != NULL) fatal("duplicate local: '%s'", l->name);
1155 l = addLabel(tstr);
1156 l->type = LB_SVAR;
1157 l->value = spt--;
1158 ++proclocals;
1159 // fix args
1160 for (l = a; l != NULL && l->type == LB_SVAR; l = l->next) --(l->value);
1161 if (nextToken() != ',') break;
1165 if (proclocals > 0) {
1166 // allocate stack
1167 OperandInfo op0;
1169 setIntOperand(&op0, -proclocals);
1170 emitInstruction(VM_POP, &op0, NULL, NULL);
1175 static void parseEndP (void) {
1176 if (!procname[0]) fatal("'endp' without 'proc'");
1177 if (nextToken() != TK_ID) fatal("identifier expected");
1178 if (strcmp(procname, tstr) != 0) fatal("endp for '%s' in proc '%s'", tstr, procname);
1179 //if (!lastWasReturn) fatal("no 'return' in proc");
1180 nextToken();
1181 procname[0] = 0;
1182 freeLocalLabels(0);
1186 static void doReturn (void) {
1187 OperandInfo op0, op1, op2;
1189 if (!procname[0]) fatal("'return' without 'proc'");
1190 lastWasReturn = 1;
1191 op0.var = op1.var = op2.var = -1;
1192 if (hasOperand()) getOperand(&op2, 0); // result
1193 setIntOperand(&op0, proclocals);
1194 setIntOperand(&op1, procargs);
1195 emitInstruction(VM_RET, &op0, &op1, &op2);
1199 static void parseLabel (void) {
1200 LabelInfo *l;
1202 if (tstr[0] != '.' && tstr[0] != '@') {
1203 if (procname[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname);
1204 freeLocalLabels(0); // vars and code
1206 if (tstr[0] == '@') {
1207 char *d = tstr;
1209 while (*d++) d[-1] = d[0];
1210 d[-1] = 0;
1212 l = findLabel(tstr);
1213 if (l != NULL) {
1214 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate label: '%s'", tstr);
1215 fixupLabelRefs(l, pc);
1216 } else {
1217 l = addLabel(tstr);
1218 l->type = LB_CODE;
1219 l->value = pc;
1221 nextToken();
1225 static void parseDB (void) {
1226 for (;;) {
1227 nextToken();
1228 if (token == TK_ID) {
1229 LabelInfo *l = findLabel(tstr);
1231 if (l == NULL) fatal("unknown label: '%s'", tstr);
1232 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1233 l->used = 1;
1234 tint = l->value;
1235 } else if (token != TK_NUM) {
1236 fatal("number expected");
1238 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1239 emitByte(tint&0xff);
1240 if (nextToken() != ',') break;
1245 static void parseDW (void) {
1246 for (;;) {
1247 nextToken();
1248 if (token == TK_ID) {
1249 LabelInfo *l = findLabel(tstr);
1251 if (l == NULL) {
1252 l = addLabel(tstr);
1253 l->type = LB_CODE;
1254 l->value = -1;
1255 //fatal("unknown label: '%s'", tstr);
1257 addLabelRef(l, pc);
1258 tint = l->value;
1259 } else if (token != TK_NUM) {
1260 fatal("number expected");
1262 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1263 emitByte(tint&0xff);
1264 emitByte((tint>>8)&0xff);
1265 if (nextToken() != ',') break;
1270 // terminator eaten
1271 static void parseAndPutString (int qch) {
1272 for (;;) {
1273 int ch = nextChar();
1275 if (ch == EOF) fatal("unterminated string");
1276 if (qch == '"') {
1277 if (ch == qch) break;
1278 if (ch == '\\') {
1279 int n;
1281 ch = nextChar();
1282 if (ch == EOF) fatal("invalid escape");
1283 switch (ch) {
1284 case 'a': emitByte('\a'); break;
1285 case 'b': emitByte('\b'); break;
1286 case 'e': emitByte('\x1b'); break;
1287 case 'f': emitByte('\f'); break;
1288 case 'n': emitByte('\n'); break;
1289 case 'r': emitByte('\r'); break;
1290 case 't': emitByte('\t'); break;
1291 case 'v': emitByte('\v'); break;
1292 case '"': case '\'': case '\\': case ' ': emitByte(ch); break;
1293 case 'x':
1294 n = digit(nextChar(), 16);
1295 if (n < 0) fatal("invalid hex escape");
1296 ch = nextChar();
1297 if (ch == EOF) fatal("invalid hex escape");
1298 if (digit(ch, 16)) {
1299 n = n*16+digit(ch, 16);
1300 } else {
1301 ungetChar(ch);
1303 emitByte(ch);
1304 break;
1305 default: fatal("invalid escape: '%c'", ch);
1307 } else {
1308 emitByte(ch);
1310 } else {
1311 if (ch == qch) {
1312 ch = nextChar();
1313 if (ch == EOF) return;
1314 if (ch == qch) {
1315 emitByte(ch);
1316 continue;
1318 ungetChar(ch);
1319 break;
1320 } else {
1321 emitByte(ch);
1328 static void parseDAscii (int zeroend) {
1329 for (;;) {
1330 int ch;
1332 skipSpaces();
1333 ch = nextChar();
1334 if (ch == EOF) break;
1335 switch (ch) {
1336 case '"': tokenWantFileName = 1; parseAndPutString(ch); tokenWantFileName = 0; break;
1337 case '\'': tokenWantFileName = 1; parseAndPutString(ch); tokenWantFileName = 0; break;
1338 default:
1339 ungetChar(ch);
1340 nextToken();
1341 if (token == TK_ID) {
1342 LabelInfo *l = findLabel(tstr);
1344 if (l == NULL) fatal("unknown label: '%s'", tstr);
1345 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1346 l->used = 1;
1347 tint = l->value;
1348 } else if (token != TK_NUM) {
1349 fatal("number expected");
1351 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1352 emitByte(tint&0xff);
1354 if (nextToken() != ',') break;
1356 if (zeroend) emitByte(0);
1360 static void process (void) {
1361 LabelInfo *l = addLabel("retval");
1362 l->type = LB_TVAR;
1363 l->value = VM_VARS_SIZE-1;
1365 procname[0] = 0;
1366 lastWasReturn = 0;
1367 nextToken();
1368 while (token != TK_EOF) {
1369 int opc;
1371 if (token == TK_LABELDEF) {
1372 // new label or operator
1373 if (strcmp(tstr, "include") == 0) {
1374 parseInlcude();
1375 } else if (strcmp(tstr, "defloc") == 0) {
1376 parseLocList();
1377 } else if (strcmp(tstr, "defgvar") == 0) {
1378 parseVarList(LB_GVAR, 0);
1379 } else if (strcmp(tstr, "defevar") == 0) { // extern global
1380 parseVarList(LB_GVAR, -1);
1381 } else if (strcmp(tstr, "deftvar") == 0) {
1382 freeLocalLabels(1); // only vars
1383 vtloc = VM_VARS_SIZE;
1384 parseVarList(LB_TVAR, 0);
1385 } else if (strcmp(tstr, "defetvar") == 0) {
1386 freeLocalLabels(1); // only vars
1387 vtloc = VM_VARS_SIZE;
1388 parseVarList(LB_TVAR, -1);
1389 } else if (strcmp(tstr, "deflvar") == 0) {
1390 parseVarList(LB_TVAR, 1);
1391 } else if (strcmp(tstr, "extern") == 0) {
1392 parseExterns();
1393 } else if (strcmp(tstr, "proc") == 0 || strcmp(tstr, "eproc") == 0) {
1394 parseProc(tstr[0] == 'e');
1395 } else if (strcmp(tstr, "endp") == 0) {
1396 parseEndP();
1397 } else if (strcmp(tstr, "const") == 0 || strcmp(tstr, "econst") == 0) {
1398 parseConst(tstr[0] == 'e');
1399 } else if (strcmp(tstr, "db") == 0) {
1400 parseDB();
1401 } else if (strcmp(tstr, "dw") == 0) {
1402 parseDW();
1403 } else if (strcmp(tstr, "da") == 0) {
1404 parseDAscii(0);
1405 } else if (strcmp(tstr, "dz") == 0) {
1406 parseDAscii(1);
1407 } else {
1408 parseLabel();
1410 continue;
1413 if (token < TK_VM_OPERATOR) fatal("mnemonics expected");
1414 opc = token;
1415 if (opc < 600) opc -= TK_VM_OPERATOR;
1416 nextToken();
1417 lastWasReturn = 0;
1418 switch (opc) {
1419 case VM_ADD:
1420 case VM_SUB:
1421 case VM_MUL:
1422 case VM_DIV:
1423 case VM_MOD:
1424 case VM_BOR:
1425 case VM_XOR:
1426 case VM_AND:
1427 doMath(opc);
1428 break;
1429 case VM_JEQ:
1430 case VM_JNE:
1431 case VM_JLT:
1432 case VM_JLE:
1433 case VM_JGT:
1434 case VM_JGE:
1435 doJXX(opc);
1436 break;
1437 case VM_JMP:
1438 doBranch(opc);
1439 break;
1440 case VM_END:
1441 doNoOperands(opc);
1442 break;
1443 case VM_BSR:
1444 doBSR(opc);
1445 break;
1446 case VM_NEW:
1447 doNew(opc);
1448 break;
1449 case VM_BRK:
1450 doNoOperands(opc);
1451 break;
1452 case VM_SET:
1453 doSet(opc);
1454 break;
1455 case VM_GET:
1456 doGet(opc);
1457 break;
1458 case VM_PSH:
1459 doPush(opc);
1460 break;
1461 case VM_POP:
1462 doPop(opc);
1463 break;
1464 case VM_SWP:
1465 doSwap(opc);
1466 break;
1467 case VM_PCK:
1468 case VM_ROL:
1469 doPickRoll(opc);
1470 break;
1471 case VM_DPT:
1472 doDepth(opc);
1473 break;
1474 case VM_TID:
1475 doTId(opc);
1476 break;
1477 case VM_KIL:
1478 case VM_SUS:
1479 case VM_RES:
1480 doKillSusRes(opc);
1481 break;
1482 case VM_STA:
1483 doSta(opc);
1484 break;
1485 case VM_RXC:
1486 doRXC(opc);
1487 break;
1488 case VM_WXC:
1489 doWXC(opc);
1490 break;
1491 case VM_RST:
1492 doRST(opc);
1493 break;
1494 case VM_MGF:
1495 case VM_MGB:
1496 doMap(opc, 1);
1497 break;
1498 case VM_MSF:
1499 case VM_MSB:
1500 doMap(opc, 0);
1501 break;
1502 case VM_RET:
1503 if (procname[0]) doReturn(); else doRet(opc);
1504 break;
1505 case VMX_DRP:
1506 doDrop(VM_POP);
1507 break;
1508 case VMX_DUP:
1509 doNoOperands(VM_PSH);
1510 break;
1511 case VMX_RTN:
1512 doRet(opc);
1513 break;
1514 case VMX_RETURN:
1515 doReturn();
1516 break;
1517 default:
1518 fatal("not yet");
1521 if (procname[0]) fatal("'proc' without 'endp': '%s'", procname);
1526 static void dumpGlobalVars (const char *fname) {
1527 FILE *fl;
1529 if (fname == NULL || !fname[0]) return;
1530 fl = fopen(fname, "w");
1531 if (!fl) return;
1532 fprintf(fl, "#ifndef _VM_GVARS_HEADER_\n");
1533 fprintf(fl, "#define _VM_GVARS_HEADER_\n\n");
1534 for (LabelInfo *l = labels; l != NULL; l = l->next) {
1535 if (l->ext) {
1536 static char name[4096];
1538 strcpy(name, l->name);
1539 for (int f = 0; name[f]; ++f) {
1540 name[f] = toupper(name[f]);
1541 if (!isalnum(name[f]) && name[f] != '_') name[f] = '_';
1543 fprintf(fl, "#define ");
1544 switch (l->type) {
1545 case LB_GVAR: fprintf(fl, "GVAR"); break;
1546 case LB_TVAR: fprintf(fl, "TVAR"); break;
1547 case LB_CODE: fprintf(fl, "CODE"); break;
1548 case LB_CONST: fprintf(fl, "CONST"); break;
1550 fprintf(fl, "_%s (%d)\n", name, l->value);
1553 fprintf(fl, "\n#endif\n");
1554 fclose(fl);
1559 ////////////////////////////////////////////////////////////////////////////////
1560 #ifdef _WIN32
1561 # include "cmdline.c"
1562 #endif
1565 ////////////////////////////////////////////////////////////////////////////////
1566 int main (int argc, char *argv[]) {
1567 #ifdef _WIN32
1568 cmdLineParse();
1569 argc = k8argc;
1570 argv = k8argv;
1571 #endif
1572 if (argc != 3) {
1573 fprintf(stderr, "usage: awasm infile outfile\n");
1574 return 1;
1576 openFile(argv[1]);
1577 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
1578 process();
1579 while (ictx != NULL) closeFile();
1580 checkLabels();
1581 //if (argc > 3) dumpGlobalVars(argv[3]);
1583 FILE *fo = fopen(argv[2], "wb");
1584 const char *sign = "AVM0";
1585 unsigned char b;
1586 int lcnt = 0;
1588 for (LabelInfo *l = labels; l != NULL; l = l->next) if (l->ext) ++lcnt;
1590 if (fo == NULL) { fprintf(stderr, "FATAL: can't create output file: '%s'\n", argv[2]); return 1; }
1592 fprintf(stderr, "%d bytes of code, %d public labels\n", pc, lcnt);
1594 fwrite(sign, 4, 1, fo);
1596 b = pc&0xff;
1597 fwrite(&b, 1, 1, fo);
1598 b = (pc>>8)&0xff;
1599 fwrite(&b, 1, 1, fo);
1601 for (int f = 0; f < pc; ++f) vmcode[f] ^= 42;
1602 fwrite(vmcode, pc, 1, fo);
1604 b = lcnt&0xff;
1605 fwrite(&b, 1, 1, fo);
1606 b = (lcnt>>8)&0xff;
1607 fwrite(&b, 1, 1, fo);
1609 for (LabelInfo *l = labels; l != NULL; l = l->next) {
1610 if (l->ext) {
1611 b = l->type;
1612 fwrite(&b, 1, 1, fo);
1614 switch (l->type) {
1615 case LB_GVAR:
1616 case LB_TVAR:
1617 b = l->value;
1618 fwrite(&b, 1, 1, fo);
1619 break;
1620 case LB_SVAR:
1621 case LB_CODE:
1622 case LB_CONST:
1623 b = l->value&0xff;
1624 fwrite(&b, 1, 1, fo);
1625 b = (l->value>>8)&0xff;
1626 fwrite(&b, 1, 1, fo);
1627 break;
1628 default:
1629 abort();
1632 b = strlen(l->name);
1633 fwrite(&b, 1, 1, fo);
1634 for (int f = 0; l->name[f]; ++f) {
1635 b = (unsigned char)(l->name[f]);
1636 b ^= 0xa5;
1637 fwrite(&b, 1, 1, fo);
1642 fclose(fo);
1644 freeLabels();
1645 return 0;