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/>.
26 ////////////////////////////////////////////////////////////////////////////////
36 static const char *ltnames
[] = {
45 ////////////////////////////////////////////////////////////////////////////////
46 #define MAX_TOKEN_LENGTH (256)
66 ////////////////////////////////////////////////////////////////////////////////
67 static uint8_t vmcode
[65536];
71 ////////////////////////////////////////////////////////////////////////////////
72 typedef struct InputContext
{
73 struct InputContext
*prev
;
82 static InputContext
*ictx
= NULL
;
84 static char tstr
[MAX_TOKEN_LENGTH
+1];
86 static int incLevel
= 0;
89 ////////////////////////////////////////////////////////////////////////////////
90 static __attribute__((__noreturn__
)) __attribute__((format(printf
, 1, 2))) void fatal (const char *fmt
, ...) {
94 fprintf(stderr
, "FATAL (line %d, file '%s'): ", ictx
->lineno
, ictx
->fname
);
96 fprintf(stderr
, "FATAL: ");
99 vfprintf(stderr
, fmt
, ap
);
101 fprintf(stderr
, "\n");
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
);
114 ic
->fname
= strdup(fname
);
116 for (char *t
= ic
->fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
121 for (int f
= 0; f
< incLevel
; ++f
) fputc(' ', stderr
);
123 fprintf(stderr
, "compiling: %s\n", ic
->fname
);
127 static void closeFile (void) {
129 InputContext
*ic
= ictx
;
133 if (ic
->fname
) free(ic
->fname
);
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) {
160 if (ictx
== NULL
) return EOF
;
161 if (ictx
->ucnt
> 0) {
162 c
= ictx
->uca
[--ictx
->ucnt
];
167 c
= (ictx
==NULL
? EOF
: '\n');
169 if (c
== '\n') ++(ictx
->lineno
);
172 if (!tokenWantFileName
) {
173 if (c
>= 'A' && c
<= 'Z') c
+= 32; // tolower
179 static void skipSpaces (void) {
183 if (c
== EOF
) return;
185 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
192 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
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;
221 if (c
< 0 || c
>= base
) return -1;
226 static void getNumber (int c
) {
233 if (c
== EOF
) return;
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;
241 if (isalpha(c
)) fatal("invalid number");
242 if (c
!= EOF
) ungetChar(c
);
246 if (digit(c
, base
) < 0) fatal("invalid number");
251 int d
= digit(c
, base
);
254 tint
= (tint
*base
)+d
;
255 if (tint
> 32767) fatal("number constant too big");
258 if (c
!= EOF
&& isalpha(c
)) fatal("invalid number");
259 if (c
!= EOF
) ungetChar(c
);
263 static int nextToken (void) {
266 memset(tstr
, 0, sizeof(tstr
));
270 if (ictx
== NULL
) return TK_EOF
;
272 if (c
== EOF
) return TK_EOF
;
273 if (c
>= 127) fatal("invalid char (%d)", c
);
277 if (isalpha(c
) || c
== '_' || c
== '$' || c
== '.' || c
== '@' || (tokenWantFileName
&& (c
== '/' || c
== '\\'))) {
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
288 //if (c != ';' && c > 32) fatal("invalid identifier");
293 if (!isalnum(tstr
[0]) && !tstr
[1]) {
294 if (c
!= EOF
&& c
> 32) ungetChar(c
);
300 if (!tokenWantFileName
) {
303 } else if (c
<= 32 && c
!= '\n') {
306 if (c
== ':') { token
= TK_LABELDEF
; break; }
307 if (c
> 32) ungetChar(c
);
308 if (c
== EOF
|| c
> 32) break;
311 if (c
!= EOF
&& c
> 32) ungetChar(c
);
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
;
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) {
331 } else if (c
== '-' || c
== '+') {
335 if ((c
= nextChar()) != EOF
) {
338 if (neg
) tint
= -tint
;
343 } else if (isdigit(c
)) {
354 ////////////////////////////////////////////////////////////////////////////////
355 typedef struct LabelInfo
{
356 struct LabelInfo
*next
;
359 int value
; // code && <0: not defined yet (forward ref)
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");
383 if (l
->fixCount
> 32760) {
384 fatal("too many label references");
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
) {
396 if (l
->fixCount
> 0) l
->used
= 1;
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;
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) {
419 LabelInfo
*l
= labels
;
422 if (l
->fixes
) free(l
->fixes
);
429 static void freeLocalLabels (int onlyVars
) {
430 LabelInfo
*p
= NULL
, *l
= labels
;
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
);
442 if (p
!= NULL
) p
->next
= n
; else labels
= 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
;
459 static LabelInfo
*addLabel (const char *name
) {
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");
480 ////////////////////////////////////////////////////////////////////////////////
482 LabelInfo
*l
; // !=NULL: label
484 int value
; // if l==NULL
485 int vartype
; // 0: global; 1: tlocal; 2: stack
489 static void setIntOperand (OperandInfo
*op
, int value
) {
497 static void setLabelOperand (OperandInfo
*op
, LabelInfo
*l
) {
505 static void getOperand (OperandInfo
*op
, int wantCode
) {
506 if (token
== TK_ID
) {
507 LabelInfo
*l
= findLabel(tstr
);
515 if (wantCode
&& l
->type
!= LB_CODE
) fatal("code offset expected");
517 setLabelOperand(op
, l
);
522 if (token
== TK_NUM
) {
523 if (wantCode
) fatal("code offset expected");
524 setIntOperand(op
, tint
);
530 //if (wantCode) fatal("code offset expected");
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
== '.') {
540 if (nextToken() != TK_NUM
) fatal("index expected");
541 setIntOperand(op
, tint
);
544 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
545 op
->vartype
= loc
=='@' ? 0 : 1;
550 } else if (token
== TK_NUM
) {
552 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
553 setIntOperand(op
, tint
);
556 fatal("invalid operand");
559 if (nextToken() != ']') fatal("']' expected");
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");
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
592 emitByte((val>>8)&0xff);
597 static void emitOperand (OperandInfo
*op
) {
598 if (!op
|| op
->var
< 0) return;
605 switch (op
->l
->type
) {
606 case LB_GVAR
: emitByte(op
->l
->value
|0x80); break;
607 case LB_TVAR
: emitByte(op
->l
->value
); break;
609 emitByte(127); // special
610 emitByte(op
->l
->value
&0xff);
611 emitByte((op
->l
->value
>>8)&0xff);
613 default: fatal("internal error");
617 switch (op
->vartype
) {
618 case 0: emitByte(op
->value
|0x80); break; // global
619 case 1: emitByte(op
->value
); break; // tlocal
621 emitByte(127); // special
622 emitByte(op
->value
&0xff);
623 emitByte((op
->value
>>8)&0xff);
625 default: fatal("internal error");
633 emitByte(255); // special
637 addLabelRef(op
->l
, pc
);
638 emitByte(op
->l
->value
&0xff);
639 emitByte((op
->l
->value
>>8)&0xff);
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
) {
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;
681 if (op2
.var
!= 1) fatal("variable expected as third operand");
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;
706 emitInstruction(opcode
, &op0
, &op1
, &op2
);
710 static void doBranch (int opcode
) {
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;
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;
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;
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;
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
) {
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;
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;
839 emitInstruction(opcode
, &op0
, &op1
, &op2
);
843 static void doSwap (int opcode
) {
844 OperandInfo op0
, op1
;
846 op0
.var
= op1
.var
= -1;
854 emitInstruction(opcode
, &op0
, &op1
, NULL
);
858 static void doDepth (int opcode
) {
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;
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;
894 emitInstruction(opcode
, &op0
, &op1
, NULL
);
898 static void doTId (int opcode
) {
902 if (hasOperand()) getOperand(&op0
, 0);
903 emitInstruction(opcode
, &op0
, NULL
, NULL
);
907 static void doKillSusRes (int opcode
) {
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;
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;
937 if (token
!= ',') fatal("at least two operands expected");
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;
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;
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;
986 if (token
!= ',') fatal("at least two operands expected");
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
);
1007 for (t
= fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
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");
1018 static void parseVarList (int type
, int local
) {
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
);
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) {
1042 if (nextToken() != TK_ID
) fatal("identifier expected");
1043 if (tstr
[0] == '.') fatal("invalid label name: '%s'", tstr
);
1044 l
= findLabel(tstr
);
1053 if (nextToken() != ',') break;
1058 static void parseLocList (void) {
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
);
1070 if (nextToken() != '=') fatal("'=' expected");
1071 if (nextToken() != TK_NUM
) fatal("number expected");
1073 if (nextToken() != ',') break;
1078 static void parseConst (int ext
) {
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
);
1086 if (nextToken() == '=') nextToken();
1087 if (token
!= TK_NUM
) fatal("constant must be numeric");
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
;
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
);
1112 freeLocalLabels(0); // vars and code
1114 l
= findLabel(tstr
);
1116 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate proc label: '%s'", tstr
);
1117 fixupLabelRefs(l
, pc
);
1123 if (ext
) l
->ext
= 1;
1127 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "arg") == 0 || strcmp(tstr
, "args") == 0)) {
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
);
1137 if (nextToken() != ',') break;
1141 // -1: return address, -2: last arg
1142 for (spt
= -2, l
= a
; l
->type
== LB_SVAR
; l
= l
->next
) {
1148 spt
= -1; // first local
1149 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "local") == 0 || strcmp(tstr
, "locals") == 0)) {
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
);
1160 for (l
= a
; l
!= NULL
&& l
->type
== LB_SVAR
; l
= l
->next
) --(l
->value
);
1161 if (nextToken() != ',') break;
1165 if (proclocals
> 0) {
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");
1186 static void doReturn (void) {
1187 OperandInfo op0
, op1
, op2
;
1189 if (!procname
[0]) fatal("'return' without 'proc'");
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) {
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] == '@') {
1209 while (*d
++) d
[-1] = d
[0];
1212 l
= findLabel(tstr
);
1214 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate label: '%s'", tstr
);
1215 fixupLabelRefs(l
, pc
);
1225 static void parseDB (void) {
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
);
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) {
1248 if (token
== TK_ID
) {
1249 LabelInfo
*l
= findLabel(tstr
);
1255 //fatal("unknown label: '%s'", tstr);
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;
1271 static void parseAndPutString (int qch
) {
1273 int ch
= nextChar();
1275 if (ch
== EOF
) fatal("unterminated string");
1277 if (ch
== qch
) break;
1282 if (ch
== EOF
) fatal("invalid escape");
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;
1294 n
= digit(nextChar(), 16);
1295 if (n
< 0) fatal("invalid hex escape");
1297 if (ch
== EOF
) fatal("invalid hex escape");
1298 if (digit(ch
, 16)) {
1299 n
= n
*16+digit(ch
, 16);
1305 default: fatal("invalid escape: '%c'", ch
);
1313 if (ch
== EOF
) return;
1328 static void parseDAscii (int zeroend
) {
1334 if (ch
== EOF
) break;
1336 case '"': tokenWantFileName
= 1; parseAndPutString(ch
); tokenWantFileName
= 0; break;
1337 case '\'': tokenWantFileName
= 1; parseAndPutString(ch
); tokenWantFileName
= 0; break;
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
);
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");
1363 l
->value
= VM_VARS_SIZE
-1;
1368 while (token
!= TK_EOF
) {
1371 if (token
== TK_LABELDEF
) {
1372 // new label or operator
1373 if (strcmp(tstr
, "include") == 0) {
1375 } else if (strcmp(tstr
, "defloc") == 0) {
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) {
1393 } else if (strcmp(tstr
, "proc") == 0 || strcmp(tstr
, "eproc") == 0) {
1394 parseProc(tstr
[0] == 'e');
1395 } else if (strcmp(tstr
, "endp") == 0) {
1397 } else if (strcmp(tstr
, "const") == 0 || strcmp(tstr
, "econst") == 0) {
1398 parseConst(tstr
[0] == 'e');
1399 } else if (strcmp(tstr
, "db") == 0) {
1401 } else if (strcmp(tstr
, "dw") == 0) {
1403 } else if (strcmp(tstr
, "da") == 0) {
1405 } else if (strcmp(tstr
, "dz") == 0) {
1413 if (token
< TK_VM_OPERATOR
) fatal("mnemonics expected");
1415 if (opc
< 600) opc
-= TK_VM_OPERATOR
;
1503 if (procname
[0]) doReturn(); else doRet(opc
);
1509 doNoOperands(VM_PSH
);
1521 if (procname
[0]) fatal("'proc' without 'endp': '%s'", procname
);
1526 static void dumpGlobalVars (const char *fname) {
1529 if (fname == NULL || !fname[0]) return;
1530 fl = fopen(fname, "w");
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) {
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 ");
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");
1559 ////////////////////////////////////////////////////////////////////////////////
1561 # include "cmdline.c"
1565 ////////////////////////////////////////////////////////////////////////////////
1566 int main (int argc
, char *argv
[]) {
1573 fprintf(stderr
, "usage: awasm infile outfile\n");
1577 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
1579 while (ictx
!= NULL
) closeFile();
1581 //if (argc > 3) dumpGlobalVars(argv[3]);
1583 FILE *fo
= fopen(argv
[2], "wb");
1584 const char *sign
= "AVM0";
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
);
1597 fwrite(&b
, 1, 1, fo
);
1599 fwrite(&b
, 1, 1, fo
);
1601 for (int f
= 0; f
< pc
; ++f
) vmcode
[f
] ^= 42;
1602 fwrite(vmcode
, pc
, 1, fo
);
1605 fwrite(&b
, 1, 1, fo
);
1607 fwrite(&b
, 1, 1, fo
);
1609 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
1612 fwrite(&b
, 1, 1, fo
);
1618 fwrite(&b
, 1, 1, fo
);
1624 fwrite(&b
, 1, 1, fo
);
1625 b
= (l
->value
>>8)&0xff;
1626 fwrite(&b
, 1, 1, fo
);
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
]);
1637 fwrite(&b
, 1, 1, fo
);