removed one warning for windoze build
[awish.git] / src / awasm.c
blobf4c8abc49825e17064d0eebb4a044e31abf94264
1 #include <ctype.h>
2 #include <stdarg.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
9 #include "vm.h"
12 ////////////////////////////////////////////////////////////////////////////////
13 enum {
14 LB_GVAR,
15 LB_TVAR,
16 LB_SVAR,
17 LB_CODE,
18 LB_CONST
22 ////////////////////////////////////////////////////////////////////////////////
23 #define MAX_TOKEN_LENGTH (256)
25 enum {
26 TK_EOF = -1,
27 TK_ID = 256,
28 TK_NUM,
29 TK_LABELDEF,
31 TK_VM_OPERATOR = 400
35 enum {
36 VMX_DRP = 600,
37 VMX_DUP,
38 VMX_RTN,
39 VMX_RETURN
43 ////////////////////////////////////////////////////////////////////////////////
44 static uint8_t vmcode[65536];
45 static int pc = 0;
48 ////////////////////////////////////////////////////////////////////////////////
49 typedef struct InputContext {
50 struct InputContext *prev;
51 FILE *fl;
52 char *fname;
53 int lineno;
54 int ucnt;
55 int uca[4];
56 } InputContext;
59 static InputContext *ictx = NULL;
60 static int token;
61 static char tstr[MAX_TOKEN_LENGTH+1];
62 static int tint;
63 static int incLevel = 0;
66 ////////////////////////////////////////////////////////////////////////////////
67 static __attribute__((__noreturn__)) __attribute__((format(printf, 1, 2))) void fatal (const char *fmt, ...) {
68 va_list ap;
70 if (ictx != NULL) {
71 fprintf(stderr, "FATAL (line %d, file '%s'): ", ictx->lineno, ictx->fname);
72 } else {
73 fprintf(stderr, "FATAL: ");
75 va_start(ap, fmt);
76 vfprintf(stderr, fmt, ap);
77 va_end(ap);
78 fprintf(stderr, "\n");
79 exit(1);
83 ////////////////////////////////////////////////////////////////////////////////
84 static void openFile (const char *fname) {
85 InputContext *ic = malloc(sizeof(InputContext));
87 if (ic == NULL) abort();
88 ic->fl = fopen(fname, "r");
89 if (ic->fl == NULL) fatal("can't open file: '%s'", fname);
90 ic->prev = ictx;
91 ic->fname = strdup(fname);
92 ic->lineno = 1;
93 ic->ucnt = 0;
94 ictx = ic;
95 for (int f = 0; f < incLevel; ++f) fputc(' ', stderr);
96 ++incLevel;
97 fprintf(stderr, "compiling: %s\n", fname);
101 static void closeFile (void) {
102 if (ictx != NULL) {
103 InputContext *ic = ictx;
105 ictx = ic->prev;
106 fclose(ic->fl);
107 if (ic->fname) free(ic->fname);
108 free(ic);
109 --incLevel;
110 if (ictx != NULL) {
111 for (int f = 0; f < incLevel-1; ++f) fputc(' ', stderr);
112 //fprintf(stderr, "compiling: %s\n", ictx->fname);
118 ////////////////////////////////////////////////////////////////////////////////
119 static void ungetChar (int c) {
120 if (c != EOF && ictx != NULL) {
121 if (ictx->ucnt >= 4) fatal("too many unread chars");
122 ictx->uca[ictx->ucnt++] = c;
127 static int tokenWantFileName = 0;
129 static int nextChar (void) {
130 int c;
132 if (ictx == NULL) return EOF;
133 if (ictx->ucnt > 0) {
134 c = ictx->uca[--ictx->ucnt];
135 } else {
136 c = fgetc(ictx->fl);
137 if (c == EOF) {
138 closeFile();
139 c = (ictx==NULL ? EOF : '\n');
140 } else {
141 if (c == '\n') ++(ictx->lineno);
144 if (!tokenWantFileName) {
145 if (c >= 'A' && c <= 'Z') c += 32; // tolower
147 return c;
151 static void skipSpaces (void) {
152 for (;;) {
153 int c = nextChar();
155 if (c == EOF) return;
156 if (c == ';') {
157 do { c = nextChar(); } while (c != EOF && c != '\n');
158 continue;
160 if (c == '/') {
161 int c1 = nextChar();
163 if (c1 == '/') {
164 do { c = nextChar(); } while (c != EOF && c != '\n');
165 continue;
167 if (c1 == '*') {
168 for (;;) {
169 c = nextChar();
170 if (c == EOF) break;
171 if (c == '*') {
172 c = nextChar();
173 if (c == '/') break;
176 continue;
178 ungetChar(c1);
179 ungetChar(c);
180 break;
182 if (c > 32) { ungetChar(c); break; }
187 static int digit (int c, int base) {
188 if (c == EOF) return -1;
189 if (c >= 'a' && c <= 'z') c -= 32;
190 if (c > '9' && c < 'A') return -1;
191 if (c > '9') c -= 7;
192 c -= '0';
193 if (c < 0 || c >= base) return -1;
194 return c;
198 static void getNumber (int c) {
199 int base = 10;
201 token = TK_NUM;
202 tint = 0;
203 if (c == '0') {
204 c = nextChar();
205 if (c == EOF) return;
206 if (!isdigit(c)) {
207 switch (c) {
208 case 'b': case 'B': base = 2; break;
209 case 'o': case 'O': base = 8; break;
210 case 'd': case 'D': base = 10; break;
211 case 'x': case 'X': base = 16; break;
212 default:
213 if (isalpha(c)) fatal("invalid number");
214 if (c != EOF) ungetChar(c);
215 return;
217 c = nextChar();
218 if (digit(c, base) < 0) fatal("invalid number");
222 for (;;) {
223 int d = digit(c, base);
225 if (d < 0) break;
226 tint = (tint*base)+d;
227 if (tint > 32767) fatal("number constant too big");
228 c = nextChar();
230 if (c != EOF && isalpha(c)) fatal("invalid number");
231 if (c != EOF) ungetChar(c);
235 static int nextToken (void) {
236 int c;
238 memset(tstr, 0, sizeof(tstr));
239 tint = 0;
240 token = TK_EOF;
241 skipSpaces();
242 if (ictx == NULL) return TK_EOF;
243 c = nextChar();
244 if (c == EOF) return TK_EOF;
245 if (c >= 127) fatal("invalid char (%d)", c);
246 tstr[0] = c;
247 tstr[1] = 0;
249 if (isalpha(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && c == '/')) {
250 // id or label
251 int f = 1;
253 for (;;) {
254 c = nextChar();
255 if (isalnum(c) || c == '_' || c == '$' || c == '.' || c == '@' || (tokenWantFileName && c == '/')) {
256 if (f >= MAX_TOKEN_LENGTH-1) fatal("identifier too long");
257 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
258 tstr[f++] = c;
259 } else {
260 //if (c != ';' && c > 32) fatal("invalid identifier");
261 break;
264 tstr[f] = 0;
265 if (!isalnum(tstr[0]) && !tstr[1]) {
266 if (c != EOF && c > 32) ungetChar(c);
267 token = tstr[0];
268 return token;
270 token = TK_ID;
271 // label definition?
272 if (!tokenWantFileName) {
273 if (c == ':') {
274 token = TK_LABELDEF;
275 } else if (c <= 32 && c != '\n') {
276 for (;;) {
277 c = nextChar();
278 if (c == ':') { token = TK_LABELDEF; break; }
279 if (c > 32) ungetChar(c);
280 if (c == EOF || c > 32) break;
282 } else {
283 if (c != EOF && c > 32) ungetChar(c);
285 // vm mnemonics?
286 if (f == 3) {
287 for (f = 0; vmOpNames[f]; ++f) {
288 if (strcmp(tstr, vmOpNames[f]) == 0) {
289 if (token == TK_LABELDEF) fatal("invalid label: '%s'", tstr);
290 token = TK_VM_OPERATOR+f;
291 break;
294 if (token < TK_VM_OPERATOR) {
295 if (strcmp(tstr, "drp") == 0) token = VMX_DRP;
296 else if (strcmp(tstr, "dup") == 0) token = VMX_DUP;
297 else if (strcmp(tstr, "rtn") == 0) token = VMX_RTN;
299 } else if (strcmp(tstr, "return") == 0) {
300 token = VMX_RETURN;
303 } else if (c == '-' || c == '+') {
304 int neg = (c=='-');
306 token = c;
307 if ((c = nextChar()) != EOF) {
308 if (isdigit(c)) {
309 getNumber(c);
310 if (neg) tint = -tint;
311 } else {
312 ungetChar(c);
315 } else if (isdigit(c)) {
316 // number
317 getNumber(c);
318 } else {
319 // delimiter
320 token = c;
322 return token;
326 ////////////////////////////////////////////////////////////////////////////////
327 typedef struct LabelInfo {
328 struct LabelInfo *next;
329 char *name;
330 int type; // LB_XXXX
331 int value; // code && <0: not defined yet (forward ref)
332 int ext; // extern?
333 int fixCount;
334 int *fixes; // 65535
335 } LabelInfo;
338 static LabelInfo *labels = NULL;
340 static int vglast = 0;
341 static int vtlast = 0;
342 static int vtloc = VM_VARS_SIZE-1; // local thread (var 126 is used for internal thing)
345 static void addLabelRef (LabelInfo *l, int pc) {
346 if (l != NULL && l->type == LB_CODE && l->value < 0) {
347 if (l->fixes == NULL) {
348 l->fixes = malloc(sizeof(int)*32768);
349 if (l->fixes == NULL) fatal("out of memory");
350 l->fixCount = 0;
352 if (l->fixCount > 32760) {
353 fatal("too many label references");
354 } else {
355 l->fixes[l->fixCount++] = pc;
357 //fprintf(stderr, "*[%s] fixup #%d at 0x%04x\n", l->name, l->fixCount-1, pc);
362 static void fixupLabelRefs (LabelInfo *l, int pc) {
363 if (l != NULL) {
364 // code fixup
365 l->value = pc;
366 for (int f = 0; f < l->fixCount; ++f) {
367 //fprintf(stderr, "[%s] FIXUP #%d: 0x%04x=0x%04x\n", l->name, f, l->fixes[f], pc);
368 vmcode[l->fixes[f]] = pc&0xff;
369 vmcode[l->fixes[f]+1] = (pc>>8)&0xff;
371 l->fixCount = 0;
376 static void checkLabels (void) {
377 for (LabelInfo *l = labels; l != NULL; l = l->next) {
378 if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
383 static void freeLabels (void) {
384 while (labels) {
385 LabelInfo *l = labels;
387 if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
388 labels = l->next;
389 if (l->fixes) free(l->fixes);
390 free(l->name);
391 free(l);
396 static void freeLocalLabels (int onlyVars) {
397 LabelInfo *p = NULL, *c = labels;
399 while (c != NULL) {
400 LabelInfo *n = c->next;
402 if ((!onlyVars || c->type != LB_CODE) && c->name[0] == '.') {
403 if (c->type == LB_CODE && c->value < 0) fatal("undefined label: '%s'", c->name);
404 if (c->fixes) free(c->fixes);
405 free(c->name);
406 free(c);
407 if (p != NULL) p->next = n; else labels = n;
408 } else {
409 p = c;
411 c = n;
416 static LabelInfo *findLabel (const char *name) {
417 if (name && name[0] == '@') ++name;
418 if (!name || !name[0]) return NULL;
419 for (LabelInfo *l = labels; l != NULL; l = l->next) if (strcmp(l->name, name) == 0) return l;
420 return NULL;
424 static LabelInfo *addLabel (const char *name) {
425 LabelInfo *l;
427 if (!name || !name[0]) abort();
428 //if (strcmp(name, "item_glovesg") == 0) fatal("!!!");
429 if (findLabel(name)) fatal("duplicate label: '%s'", name);
430 l = malloc(sizeof(LabelInfo));
431 if (l == NULL) fatal("out of memory");
432 l->name = strdup(name);
433 if (l->name == NULL) fatal("out of memory");
434 l->type = LB_CODE;
435 l->value = -1;
436 l->ext = 0;
437 l->fixCount = 0;
438 l->fixes = NULL;
439 l->next = labels;
440 labels = l;
441 return l;
445 ////////////////////////////////////////////////////////////////////////////////
446 typedef struct {
447 LabelInfo *l; // !=NULL: label
448 int var; // [...]
449 int value; // if l==NULL
450 int vartype; // 0: global; 1: tlocal; 2: stack
451 } OperandInfo;
454 static void setIntOperand (OperandInfo *op, int value) {
455 op->l = NULL;
456 op->var = 0;
457 op->value = value;
458 op->vartype = -1;
462 static void setLabelOperand (OperandInfo *op, LabelInfo *l) {
463 op->l = l;
464 op->var = 0;
465 op->value = 0;
466 op->vartype = -1;
470 static void getOperand (OperandInfo *op, int wantCode) {
471 if (token == TK_ID) {
472 LabelInfo *l = findLabel(tstr);
474 if (l == NULL) {
475 // new code label
476 l = addLabel(tstr);
477 l->type = LB_CODE;
478 l->value = -1;
479 } else {
480 if (wantCode && l->type != LB_CODE) fatal("code offset expected");
482 setLabelOperand(op, l);
483 nextToken();
484 return;
487 if (token == TK_NUM) {
488 if (wantCode) fatal("code offset expected");
489 setIntOperand(op, tint);
490 nextToken();
491 return;
494 if (token == '[') {
495 //if (wantCode) fatal("code offset expected");
496 nextToken();
497 if (token == TK_ID) {
498 LabelInfo *l = findLabel(tstr);
500 if (l == NULL || l->type == LB_CODE) fatal("unknown variable: '%s'", tstr);
501 setLabelOperand(op, l);
502 } else if (token == '@' || token == '.') {
503 int loc = token;
505 if (nextToken() != TK_NUM) fatal("index expected");
506 setIntOperand(op, tint);
507 if (loc != '.') {
508 // not stack
509 if (tint < 0 || tint > 126) fatal("invalid variable index");
510 op->vartype = loc=='@' ? 0 : 1;
511 } else {
512 // stack
513 op->vartype = 2;
515 } else if (token == TK_NUM) {
516 // local
517 if (tint < 0 || tint > 126) fatal("invalid variable index");
518 setIntOperand(op, tint);
519 op->vartype = 1;
520 } else {
521 fatal("invalid operand");
523 op->var = 1;
524 if (nextToken() != ']') fatal("']' expected");
525 nextToken();
526 return;
529 fprintf(stderr, "*%d [%s] %d\n", token, tstr, tint);
530 fatal("invalid operand");
534 static inline int hasOperand (void) {
535 return (token != TK_EOF && token != TK_LABELDEF && token < TK_VM_OPERATOR);
539 ////////////////////////////////////////////////////////////////////////////////
540 static void emitByte (int b) {
541 if (b < 0 || b > 255) fatal("internal error");
542 if (pc >= 32768) fatal("code too big");
543 vmcode[pc++] = b;
547 static void emitOpCode (int opc, int opcount) {
548 if (opc < 0 || opc > 63 || opcount < 0 || opcount > 3) fatal("internal error");
549 emitByte(opc|(opcount<<6));
554 static void emitInteger (int val) {
555 emitByte(255); // special
556 emitByte(val&0xff);
557 emitByte((val>>8)&0xff);
562 static void emitOperand (OperandInfo *op) {
563 if (!op || op->var < 0) return;
565 if (op->var > 0) {
566 // variable
567 if (op->l) {
568 // from label
569 switch (op->l->type) {
570 case LB_GVAR: emitByte(op->l->value|0x80); break;
571 case LB_TVAR: emitByte(op->l->value); break;
572 case LB_SVAR:
573 emitByte(127); // special
574 emitByte(op->l->value&0xff);
575 emitByte((op->l->value>>8)&0xff);
576 break;
577 default: fatal("internal error");
579 } else {
580 // from value
581 switch (op->vartype) {
582 case 0: emitByte(op->value|0x80); break;
583 case 1: emitByte(op->value); break;
584 case 2:
585 emitByte(127); // special
586 emitByte(op->value&0xff);
587 emitByte((op->value>>8)&0xff);
588 break;
589 default: fatal("internal error");
592 return;
595 if (op->var == 0) {
596 // immediate
597 emitByte(255); // special
598 if (op->l) {
599 // from label
600 if (op->l->type == LB_CODE && op->l->value < 0) {
601 // undefined
602 addLabelRef(op->l, pc);
603 emitByte(0);
604 emitByte(0);
605 } else {
606 // defined
607 emitByte(op->l->value&0xff);
608 emitByte((op->l->value>>8)&0xff);
610 } else {
611 // direct
612 if (op->value < -32767 || op->value > 32767) fatal("invalid value");
613 emitByte(op->value&0xff);
614 emitByte((op->value>>8)&0xff);
620 static void emitInstruction (int opc, OperandInfo *op0, OperandInfo *op1, OperandInfo *op2) {
621 int ocnt = 0;
623 if (op0 && op0->var >= 0) ++ocnt;
624 if (ocnt == 1 && op1 && op1->var >= 0) ++ocnt;
625 if (ocnt == 2 && op2 && op2->var >= 0) ++ocnt;
626 emitOpCode(opc, ocnt);
627 if (ocnt > 0) emitOperand(op0);
628 if (ocnt > 1) emitOperand(op1);
629 if (ocnt > 2) emitOperand(op2);
633 ////////////////////////////////////////////////////////////////////////////////
634 static void doNoOperands (int opcode) {
635 emitInstruction(opcode, NULL, NULL, NULL);
639 static void doMath (int opcode) {
640 OperandInfo op0, op1, op2;
642 op0.var = op1.var = op2.var = -1;
643 if (hasOperand()) {
644 getOperand(&op0, 0);
645 if (token == ',') {
646 nextToken();
647 getOperand(&op1, 0);
648 if (token == ',') {
649 nextToken();
650 getOperand(&op2, 0);
651 if (op2.var != 1) fatal("variable expected as third operand");
652 } else {
653 if (op0.var != 1) fatal("variable expected as first operand");
657 emitInstruction(opcode, &op0, &op1, &op2);
661 static void doJXX (int opcode) {
662 OperandInfo op0, op1, op2;
664 op0.var = op1.var = op2.var = -1;
665 if (hasOperand()) {
666 getOperand(&op0, 1);
667 if (token == ',') {
668 nextToken();
669 getOperand(&op1, 0);
670 if (token == ',') {
671 nextToken();
672 getOperand(&op2, 0);
676 emitInstruction(opcode, &op0, &op1, &op2);
680 static void doBranch (int opcode) {
681 OperandInfo op0;
683 op0.var = -1;
684 if (hasOperand()) {
685 getOperand(&op0, 1);
687 emitInstruction(opcode, &op0, NULL, NULL);
691 static void doBSR (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 doMap (int opcode, int lastMBV) {
711 OperandInfo op0, op1, op2;
713 op0.var = op1.var = op2.var = -1;
714 if (hasOperand()) {
715 getOperand(&op0, 0);
716 if (token == ',') {
717 nextToken();
718 getOperand(&op1, 0);
719 if (token == ',') {
720 nextToken();
721 getOperand(&op2, 0);
725 emitInstruction(opcode, &op0, &op1, &op2);
729 static void doRST (int opcode) {
730 OperandInfo op0, op1, op2;
732 op0.var = op1.var = op2.var = -1;
733 if (hasOperand()) {
734 getOperand(&op0, 0);
735 if (token == ',') {
736 nextToken();
737 getOperand(&op1, 0);
738 if (token == ',') {
739 nextToken();
740 getOperand(&op2, 0);
744 emitInstruction(opcode, &op0, &op1, &op2);
748 static void doSet (int opcode) {
749 OperandInfo op0, op1, op2;
751 op0.var = op1.var = op2.var = -1;
752 getOperand(&op0, 0);
753 if (token == ',') {
754 nextToken();
755 getOperand(&op1, 0);
756 if (token == ',') {
757 nextToken();
758 getOperand(&op2, 0);
761 if (op2.var < 0 && op0.var != 1) fatal("first operand should be var");
762 emitInstruction(opcode, &op0, &op1, &op2);
766 static void doDrop (int opcode) {
767 OperandInfo op0;
769 op0.var = -1;
770 if (hasOperand()) getOperand(&op0, 0);
771 if (op0.var > 0) fatal("number expected");
772 if (op0.value < 0) fatal("positive number expected");
773 emitInstruction(opcode, &op0, NULL, NULL);
777 static void doPush (int opcode) {
778 OperandInfo op0, op1, op2;
780 op0.var = op1.var = op2.var = -1;
781 getOperand(&op0, 0);
782 if (token == ',') {
783 nextToken();
784 getOperand(&op1, 0);
785 if (token == ',') {
786 nextToken();
787 getOperand(&op2, 0);
790 emitInstruction(opcode, &op0, &op1, &op2);
794 static void doPop (int opcode) {
795 OperandInfo op0, op1, op2;
797 op0.var = op1.var = op2.var = -1;
798 if (hasOperand()) {
799 getOperand(&op0, 0);
800 if (token == ',') {
801 nextToken();
802 getOperand(&op1, 0);
803 if (token == ',') {
804 nextToken();
805 getOperand(&op2, 0);
809 emitInstruction(opcode, &op0, &op1, &op2);
813 static void doSwap (int opcode) {
814 OperandInfo op0, op1;
816 op0.var = op1.var = -1;
817 if (hasOperand()) {
818 getOperand(&op0, 0);
819 if (token == ',') {
820 nextToken();
821 getOperand(&op1, 0);
824 emitInstruction(opcode, &op0, &op1, NULL);
828 static void doDepth (int opcode) {
829 OperandInfo op0;
831 op0.var = -1;
832 if (hasOperand()) getOperand(&op0, 0);
833 emitInstruction(opcode, &op0, NULL, NULL);
837 static void doPickRoll (int opcode) {
838 OperandInfo op0, op1;
840 op0.var = op1.var = -1;
841 if (hasOperand()) {
842 getOperand(&op0, 0);
843 if (token == ',') {
844 nextToken();
845 getOperand(&op1, 0);
846 if (op1.var != 1) fatal("second argument must be variable");
849 emitInstruction(opcode, &op0, &op1, NULL);
853 static void doNew (int opcode) {
854 OperandInfo op0, op1;
856 op0.var = op1.var = -1;
857 if (hasOperand()) {
858 getOperand(&op0, 0);
859 if (token == ',') {
860 nextToken();
861 getOperand(&op1, 0);
864 emitInstruction(opcode, &op0, &op1, NULL);
868 static void doTId (int opcode) {
869 OperandInfo op0;
871 op0.var = -1;
872 if (hasOperand()) getOperand(&op0, 0);
873 emitInstruction(opcode, &op0, NULL, NULL);
877 static void doKillSusRes (int opcode) {
878 OperandInfo op0;
880 op0.var = -1;
881 if (hasOperand()) getOperand(&op0, 0);
882 emitInstruction(opcode, &op0, NULL, NULL);
886 static void doSta (int opcode) {
887 OperandInfo op0, op1;
889 op0.var = op1.var = -1;
890 if (hasOperand()) {
891 getOperand(&op0, 0);
892 if (token == ',') {
893 nextToken();
894 getOperand(&op1, 0);
895 if (op1.var != 1) fatal("variable expected");
898 emitInstruction(opcode, &op0, &op1, NULL);
902 static void doGet (int opcode) {
903 OperandInfo op0, op1, op2;
905 op0.var = op1.var = op2.var = -1;
906 getOperand(&op0, 0);
907 if (token != ',') fatal("at least two operands expected");
908 nextToken();
909 getOperand(&op1, 0);
910 if (token == ',') {
911 nextToken();
912 getOperand(&op2, 0);
913 if (op2.var != 1) fatal("variable expected as third operand");
915 emitInstruction(opcode, &op0, &op1, &op2);
919 static void doRXC (int opcode) {
920 OperandInfo op0, op1;
922 op0.var = op1.var = -1;
923 if (hasOperand()) {
924 getOperand(&op0, 0);
925 if (token == ',') {
926 nextToken();
927 getOperand(&op1, 0);
928 if (op1.var != 1) fatal("variable expected as second operand");
931 emitInstruction(opcode, &op0, &op1, NULL);
935 static void doWXC (int opcode) {
936 OperandInfo op0, op1;
938 op0.var = op1.var = -1;
939 if (hasOperand()) {
940 getOperand(&op0, 0);
941 if (token == ',') {
942 nextToken();
943 getOperand(&op1, 0);
946 emitInstruction(opcode, &op0, &op1, NULL);
950 static void doRet (int opcode) {
951 OperandInfo op0, op1, op2;
953 op0.var = op1.var = op2.var = -1;
954 if (hasOperand()) {
955 getOperand(&op0, 0);
956 if (token != ',') fatal("at least two operands expected");
957 nextToken();
958 getOperand(&op1, 0);
959 if (token == ',') {
960 nextToken();
961 getOperand(&op2, 0);
964 emitInstruction(opcode, &op0, &op1, &op2);
968 ////////////////////////////////////////////////////////////////////////////////
969 static void parseInlcude (void) {
970 static char fname[8192], *t;
972 tokenWantFileName = 1;
973 if (nextToken() != TK_ID) fatal("identifier expected");
974 tokenWantFileName = 0;
975 strcpy(fname, ictx->fname);
976 t = strrchr(fname, '/');
977 if (t != NULL) t[1] = 0; else fname[0] = 0;
978 strcat(fname, tstr);
979 if (incLevel > 64) fatal("too many includes");
980 openFile(fname);
981 nextToken();
985 static void parseVarList (int type, int local) {
986 for (;;) {
987 LabelInfo *l;
989 if (nextToken() != TK_ID) fatal("identifier expected");
990 if (tstr[0] == '.' && local <= 0) fatal("invalid variable name: '%s'", tstr);
991 if (tstr[0] != '.' && local > 0) fatal("invalid variable name: '%s'", tstr);
992 l = findLabel(tstr);
993 if (l != NULL) fatal("duplicate variable or label: '%s'", tstr);
994 l = addLabel(tstr);
995 l->type = type;
996 if (local > 0 && vtloc <= vtlast) fatal("too many local vars");
997 l->value = local>0?(--vtloc):(type==LB_GVAR?vglast++:vtlast++);
998 if (local < 0) l->ext = 1;
999 if (l->value > 126) fatal("too many vars");
1000 if (nextToken() != ',') break;
1005 static void parseExterns (void) {
1006 for (;;) {
1007 LabelInfo *l;
1009 if (nextToken() != TK_ID) fatal("identifier expected");
1010 if (tstr[0] == '.') fatal("invalid label name: '%s'", tstr);
1011 l = findLabel(tstr);
1012 if (l != NULL) {
1013 l->ext = 1;
1014 } else {
1015 l = addLabel(tstr);
1016 l->ext = 1;
1017 l->type = LB_CODE;
1018 l->value = -1;
1020 if (nextToken() != ',') break;
1025 static void parseLocList (void) {
1026 for (;;) {
1027 LabelInfo *l;
1029 if (nextToken() != TK_ID) fatal("identifier expected");
1030 if (tstr[0] != '.') fatal("local identifier expected instead of '%s'", tstr);
1031 l = findLabel(tstr);
1032 if (l != NULL) fatal("can't redefine label as local: '%s'", tstr);
1033 l = addLabel(tstr);
1034 l->ext = 0;
1035 l->type = LB_SVAR;
1036 l->value = -1;
1037 if (nextToken() != '=') fatal("'=' expected");
1038 if (nextToken() != TK_NUM) fatal("number expected");
1039 l->value = tint;
1040 if (nextToken() != ',') break;
1045 static void parseConst (int ext) {
1046 LabelInfo *l;
1048 if (nextToken() != TK_ID) fatal("identifier expected");
1049 if (tstr[0] == '.') fatal("invalid constant name: '%s'", tstr);
1050 l = findLabel(tstr);
1051 if (l != NULL) fatal("constant must be unique: '%s'", tstr);
1052 l = addLabel(tstr);
1053 if (nextToken() == '=') nextToken();
1054 if (token != TK_NUM) fatal("constant must be numeric");
1055 l->type = LB_CONST;
1056 l->value = tint;
1057 l->ext = ext;
1058 nextToken();
1062 static char procname[8192];
1063 static int proclocals = 0;
1064 static int procargs = 0;
1065 static int lastWasReturn = 0;
1068 static void parseProc (int ext) {
1069 LabelInfo *l, *a = NULL;
1070 int spt;
1072 lastWasReturn = 0;
1073 if (nextToken() != TK_ID) fatal("identifier expected");
1074 if (tstr[0] == '.') fatal("invalid proc name: '%s'", tstr);
1075 if (procname[0]) fatal("unclosed proc: '%s'", procname);
1076 strcpy(procname, tstr);
1077 proclocals = 0;
1078 procargs = 0;
1079 freeLocalLabels(0); // vars and code
1081 l = findLabel(tstr);
1082 if (l != NULL) {
1083 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate proc label: '%s'", tstr);
1084 fixupLabelRefs(l, pc);
1085 } else {
1086 l = addLabel(tstr);
1087 l->type = LB_CODE;
1088 l->value = pc;
1090 if (ext) l->ext = 1;
1092 nextToken();
1094 while (token == TK_LABELDEF && (strcmp(tstr, "arg") == 0 || strcmp(tstr, "args") == 0)) {
1095 for (;;) {
1096 if (nextToken() != TK_ID) fatal("identifier expected");
1097 if (tstr[0] != '.') fatal("argument name must starts with '.'");
1098 l = findLabel(tstr);
1099 if (l != NULL) fatal("duplicate argument: '%s'", l->name);
1100 l = addLabel(tstr);
1101 l->type = LB_SVAR;
1102 l->value = 666;
1103 ++procargs;
1104 if (nextToken() != ',') break;
1106 a = labels; //HACK!
1107 // fix values
1108 // -1: return address, -2: last arg
1109 for (spt = -2, l = a; l->type == LB_SVAR; l = l->next) {
1110 l->value = spt;
1111 --spt;
1115 spt = -1; // first local
1116 while (token == TK_LABELDEF && (strcmp(tstr, "local") == 0 || strcmp(tstr, "locals") == 0)) {
1117 for (;;) {
1118 if (nextToken() != TK_ID) fatal("identifier expected");
1119 if (tstr[0] != '.') fatal("local variable name must starts with '.'");
1120 l = findLabel(tstr);
1121 if (l != NULL) fatal("duplicate local: '%s'", l->name);
1122 l = addLabel(tstr);
1123 l->type = LB_SVAR;
1124 l->value = spt--;
1125 ++proclocals;
1126 // fix args
1127 for (l = a; l != NULL && l->type == LB_SVAR; l = l->next) --(l->value);
1128 if (nextToken() != ',') break;
1132 if (proclocals > 0) {
1133 // allocate stack
1134 OperandInfo op0;
1136 setIntOperand(&op0, -proclocals);
1137 emitInstruction(VM_POP, &op0, NULL, NULL);
1142 static void parseEndP (void) {
1143 if (!procname[0]) fatal("'endp' without 'proc'");
1144 if (nextToken() != TK_ID) fatal("identifier expected");
1145 if (strcmp(procname, tstr) != 0) fatal("endp for '%s' in proc '%s'", tstr, procname);
1146 //if (!lastWasReturn) fatal("no 'return' in proc");
1147 nextToken();
1148 procname[0] = 0;
1149 freeLocalLabels(0);
1153 static void doReturn (void) {
1154 OperandInfo op0, op1, op2;
1156 if (!procname[0]) fatal("'return' without 'proc'");
1157 lastWasReturn = 1;
1158 op0.var = op1.var = op2.var = -1;
1159 if (hasOperand()) getOperand(&op2, 0); // result
1160 setIntOperand(&op0, proclocals);
1161 setIntOperand(&op1, procargs);
1162 emitInstruction(VM_RET, &op0, &op1, &op2);
1166 static void parseLabel (void) {
1167 LabelInfo *l;
1169 if (tstr[0] != '.' && tstr[0] != '@') {
1170 if (procname[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname);
1171 freeLocalLabels(0); // vars and code
1173 if (tstr[0] == '@') {
1174 char *d = tstr;
1176 while (*d++) d[-1] = d[0];
1177 d[-1] = 0;
1179 l = findLabel(tstr);
1180 if (l != NULL) {
1181 if (l->type != LB_CODE || l->value >= 0) fatal("duplicate label: '%s'", tstr);
1182 fixupLabelRefs(l, pc);
1183 } else {
1184 l = addLabel(tstr);
1185 l->type = LB_CODE;
1186 l->value = pc;
1188 nextToken();
1192 static void parseDB (void) {
1193 for (;;) {
1194 nextToken();
1195 if (token == TK_ID) {
1196 LabelInfo *l = findLabel(tstr);
1198 if (l == NULL) fatal("unknown label: '%s'", tstr);
1199 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1200 tint = l->value;
1201 } else if (token != TK_NUM) {
1202 fatal("number expected");
1204 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1205 emitByte(tint&0xff);
1206 if (nextToken() != ',') break;
1211 static void parseDW (void) {
1212 for (;;) {
1213 nextToken();
1214 if (token == TK_ID) {
1215 LabelInfo *l = findLabel(tstr);
1217 if (l == NULL) {
1218 l = addLabel(tstr);
1219 l->type = LB_CODE;
1220 l->value = -1;
1221 //fatal("unknown label: '%s'", tstr);
1223 addLabelRef(l, pc);
1224 tint = l->value;
1225 } else if (token != TK_NUM) {
1226 fatal("number expected");
1228 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1229 emitByte(tint&0xff);
1230 emitByte((tint>>8)&0xff);
1231 if (nextToken() != ',') break;
1236 // terminator eaten
1237 static void parseAndPutString (int qch) {
1238 for (;;) {
1239 int ch = nextChar();
1241 if (ch == EOF) fatal("unterminated string");
1242 if (qch == '"') {
1243 if (ch == qch) break;
1244 if (ch == '\\') {
1245 int n;
1247 ch = nextChar();
1248 if (ch == EOF) fatal("invalid escape");
1249 switch (ch) {
1250 case 'a': emitByte('\a'); break;
1251 case 'b': emitByte('\b'); break;
1252 case 'e': emitByte('\x1b'); break;
1253 case 'f': emitByte('\f'); break;
1254 case 'n': emitByte('\n'); break;
1255 case 'r': emitByte('\r'); break;
1256 case 't': emitByte('\t'); break;
1257 case 'v': emitByte('\v'); break;
1258 case '"': case '\'': case '\\': case ' ': emitByte(ch); break;
1259 case 'x':
1260 n = digit(nextChar(), 16);
1261 if (n < 0) fatal("invalid hex escape");
1262 ch = nextChar();
1263 if (ch == EOF) fatal("invalid hex escape");
1264 if (digit(ch, 16)) {
1265 n = n*16+digit(ch, 16);
1266 } else {
1267 ungetChar(ch);
1269 emitByte(ch);
1270 break;
1271 default: fatal("invalid escape: '%c'", ch);
1273 } else {
1274 emitByte(ch);
1276 } else {
1277 if (ch == qch) {
1278 ch = nextChar();
1279 if (ch == EOF) return;
1280 if (ch == qch) {
1281 emitByte(ch);
1282 continue;
1284 ungetChar(ch);
1285 break;
1286 } else {
1287 emitByte(ch);
1294 static void parseDAscii (int zeroend) {
1295 for (;;) {
1296 int ch;
1298 skipSpaces();
1299 ch = nextChar();
1300 if (ch == EOF) break;
1301 switch (ch) {
1302 case '"': tokenWantFileName = 1; parseAndPutString(ch); tokenWantFileName = 0; break;
1303 case '\'': tokenWantFileName = 1; parseAndPutString(ch); tokenWantFileName = 0; break;
1304 default:
1305 ungetChar(ch);
1306 nextToken();
1307 if (token == TK_ID) {
1308 LabelInfo *l = findLabel(tstr);
1310 if (l == NULL) fatal("unknown label: '%s'", tstr);
1311 if (l->type == LB_CODE) fatal("bad label type: '%s'", l->name);
1312 tint = l->value;
1313 } else if (token != TK_NUM) {
1314 fatal("number expected");
1316 if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1317 emitByte(tint&0xff);
1319 if (nextToken() != ',') break;
1321 if (zeroend) emitByte(0);
1325 static void process (void) {
1326 LabelInfo *l = addLabel("retval");
1327 l->type = LB_TVAR;
1328 l->value = VM_VARS_SIZE-1;
1330 procname[0] = 0;
1331 lastWasReturn = 0;
1332 nextToken();
1333 while (token != TK_EOF) {
1334 int opc;
1336 if (token == TK_LABELDEF) {
1337 // new label or operator
1338 if (strcmp(tstr, "include") == 0) {
1339 parseInlcude();
1340 } else if (strcmp(tstr, "defloc") == 0) {
1341 parseLocList();
1342 } else if (strcmp(tstr, "defgvar") == 0) {
1343 parseVarList(LB_GVAR, 0);
1344 } else if (strcmp(tstr, "defevar") == 0) { // extern global
1345 parseVarList(LB_GVAR, -1);
1346 } else if (strcmp(tstr, "deftvar") == 0) {
1347 freeLocalLabels(1); // only vars
1348 vtloc = VM_VARS_SIZE;
1349 parseVarList(LB_TVAR, 0);
1350 } else if (strcmp(tstr, "defetvar") == 0) {
1351 freeLocalLabels(1); // only vars
1352 vtloc = VM_VARS_SIZE;
1353 parseVarList(LB_TVAR, -1);
1354 } else if (strcmp(tstr, "deflvar") == 0) {
1355 parseVarList(LB_TVAR, 1);
1356 } else if (strcmp(tstr, "extern") == 0) {
1357 parseExterns();
1358 } else if (strcmp(tstr, "proc") == 0 || strcmp(tstr, "eproc") == 0) {
1359 parseProc(tstr[0] == 'e');
1360 } else if (strcmp(tstr, "endp") == 0) {
1361 parseEndP();
1362 } else if (strcmp(tstr, "const") == 0 || strcmp(tstr, "econst") == 0) {
1363 parseConst(tstr[0] == 'e');
1364 } else if (strcmp(tstr, "db") == 0) {
1365 parseDB();
1366 } else if (strcmp(tstr, "dw") == 0) {
1367 parseDW();
1368 } else if (strcmp(tstr, "da") == 0) {
1369 parseDAscii(0);
1370 } else if (strcmp(tstr, "dz") == 0) {
1371 parseDAscii(1);
1372 } else {
1373 parseLabel();
1375 continue;
1378 if (token < TK_VM_OPERATOR) fatal("mnemonics expected");
1379 opc = token;
1380 if (opc < 600) opc -= TK_VM_OPERATOR;
1381 nextToken();
1382 lastWasReturn = 0;
1383 switch (opc) {
1384 case VM_ADD:
1385 case VM_SUB:
1386 case VM_MUL:
1387 case VM_DIV:
1388 case VM_MOD:
1389 case VM_BOR:
1390 case VM_XOR:
1391 case VM_AND:
1392 doMath(opc);
1393 break;
1394 case VM_JEQ:
1395 case VM_JNE:
1396 case VM_JLT:
1397 case VM_JLE:
1398 case VM_JGT:
1399 case VM_JGE:
1400 doJXX(opc);
1401 break;
1402 case VM_JMP:
1403 doBranch(opc);
1404 break;
1405 case VM_END:
1406 doNoOperands(opc);
1407 break;
1408 case VM_BSR:
1409 doBSR(opc);
1410 break;
1411 case VM_NEW:
1412 doNew(opc);
1413 break;
1414 case VM_BRK:
1415 doNoOperands(opc);
1416 break;
1417 case VM_SET:
1418 doSet(opc);
1419 break;
1420 case VM_GET:
1421 doGet(opc);
1422 break;
1423 case VM_PSH:
1424 doPush(opc);
1425 break;
1426 case VM_POP:
1427 doPop(opc);
1428 break;
1429 case VM_SWP:
1430 doSwap(opc);
1431 break;
1432 case VM_PCK:
1433 case VM_ROL:
1434 doPickRoll(opc);
1435 break;
1436 case VM_DPT:
1437 doDepth(opc);
1438 break;
1439 case VM_TID:
1440 doTId(opc);
1441 break;
1442 case VM_KIL:
1443 case VM_SUS:
1444 case VM_RES:
1445 doKillSusRes(opc);
1446 break;
1447 case VM_STA:
1448 doSta(opc);
1449 break;
1450 case VM_RXC:
1451 doRXC(opc);
1452 break;
1453 case VM_WXC:
1454 doWXC(opc);
1455 break;
1456 case VM_RST:
1457 doRST(opc);
1458 break;
1459 case VM_MGF:
1460 case VM_MGB:
1461 doMap(opc, 1);
1462 break;
1463 case VM_MSF:
1464 case VM_MSB:
1465 doMap(opc, 0);
1466 break;
1467 case VM_RET:
1468 if (procname[0]) doReturn(); else doRet(opc);
1469 break;
1470 case VMX_DRP:
1471 doDrop(VM_POP);
1472 break;
1473 case VMX_DUP:
1474 doNoOperands(VM_PSH);
1475 break;
1476 case VMX_RTN:
1477 doRet(opc);
1478 break;
1479 case VMX_RETURN:
1480 doReturn();
1481 break;
1482 default:
1483 fatal("not yet");
1486 if (procname[0]) fatal("'proc' without 'endp': '%s'", procname);
1491 static void dumpGlobalVars (const char *fname) {
1492 FILE *fl;
1494 if (fname == NULL || !fname[0]) return;
1495 fl = fopen(fname, "w");
1496 if (!fl) return;
1497 fprintf(fl, "#ifndef _VM_GVARS_HEADER_\n");
1498 fprintf(fl, "#define _VM_GVARS_HEADER_\n\n");
1499 for (LabelInfo *l = labels; l != NULL; l = l->next) {
1500 if (l->ext) {
1501 static char name[4096];
1503 strcpy(name, l->name);
1504 for (int f = 0; name[f]; ++f) {
1505 name[f] = toupper(name[f]);
1506 if (!isalnum(name[f]) && name[f] != '_') name[f] = '_';
1508 fprintf(fl, "#define ");
1509 switch (l->type) {
1510 case LB_GVAR: fprintf(fl, "GVAR"); break;
1511 case LB_TVAR: fprintf(fl, "TVAR"); break;
1512 case LB_CODE: fprintf(fl, "CODE"); break;
1513 case LB_CONST: fprintf(fl, "CONST"); break;
1515 fprintf(fl, "_%s (%d)\n", name, l->value);
1518 fprintf(fl, "\n#endif\n");
1519 fclose(fl);
1524 ////////////////////////////////////////////////////////////////////////////////
1525 int main (int argc, char *argv[]) {
1526 if (argc != 3) {
1527 fprintf(stderr, "usage: awasm infile outfile\n");
1528 return 1;
1530 openFile(argv[1]);
1531 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
1532 process();
1533 while (ictx != NULL) closeFile();
1534 checkLabels();
1535 //if (argc > 3) dumpGlobalVars(argv[3]);
1537 FILE *fo = fopen(argv[2], "wb");
1538 const char *sign = "AVM0";
1539 unsigned char b;
1540 int lcnt = 0;
1542 for (LabelInfo *l = labels; l != NULL; l = l->next) if (l->ext) ++lcnt;
1544 if (fo == NULL) { fprintf(stderr, "FATAL: can't create output file: '%s'\n", argv[2]); return 1; }
1546 printf("%d bytes of code, %d public labels\n", pc, lcnt);
1548 fwrite(sign, 4, 1, fo);
1550 b = pc&0xff;
1551 fwrite(&b, 1, 1, fo);
1552 b = (pc>>8)&0xff;
1553 fwrite(&b, 1, 1, fo);
1555 for (int f = 0; f < pc; ++f) vmcode[f] ^= 42;
1556 fwrite(vmcode, pc, 1, fo);
1558 b = lcnt&0xff;
1559 fwrite(&b, 1, 1, fo);
1560 b = (lcnt>>8)&0xff;
1561 fwrite(&b, 1, 1, fo);
1563 for (LabelInfo *l = labels; l != NULL; l = l->next) {
1564 if (l->ext) {
1565 b = l->type;
1566 fwrite(&b, 1, 1, fo);
1568 switch (l->type) {
1569 case LB_GVAR:
1570 case LB_TVAR:
1571 b = l->value;
1572 fwrite(&b, 1, 1, fo);
1573 break;
1574 case LB_SVAR:
1575 case LB_CODE:
1576 case LB_CONST:
1577 b = l->value&0xff;
1578 fwrite(&b, 1, 1, fo);
1579 b = (l->value>>8)&0xff;
1580 fwrite(&b, 1, 1, fo);
1581 break;
1582 default:
1583 abort();
1586 b = strlen(l->name);
1587 fwrite(&b, 1, 1, fo);
1588 for (int f = 0; l->name[f]; ++f) {
1589 b = (unsigned char)(l->name[f]);
1590 b ^= 0xa5;
1591 fwrite(&b, 1, 1, fo);
1596 fclose(fo);
1598 freeLabels();
1599 return 0;