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 ////////////////////////////////////////////////////////////////////////////////
30 ////////////////////////////////////////////////////////////////////////////////
40 static const char *ltnames
[] = {
49 ////////////////////////////////////////////////////////////////////////////////
50 #define MAX_TOKEN_LENGTH (256)
70 ////////////////////////////////////////////////////////////////////////////////
71 static uint8_t vmcode
[65536];
75 ////////////////////////////////////////////////////////////////////////////////
76 typedef struct InputContext
{
77 struct InputContext
*prev
;
86 static InputContext
*ictx
= NULL
;
88 static char tstr
[MAX_TOKEN_LENGTH
+1];
90 static int incLevel
= 0;
93 ////////////////////////////////////////////////////////////////////////////////
94 static __attribute__((__noreturn__
)) __attribute__((format(printf
, 1, 2))) void fatal (const char *fmt
, ...) {
98 fprintf(stderr
, "FATAL (line %d, file '%s'): ", ictx
->lineno
, ictx
->fname
);
100 fprintf(stderr
, "FATAL: ");
103 vfprintf(stderr
, fmt
, ap
);
105 fprintf(stderr
, "\n");
110 ////////////////////////////////////////////////////////////////////////////////
111 static void openFile (const char *fname
) {
112 InputContext
*ic
= malloc(sizeof(InputContext
));
114 if (ic
== NULL
) abort();
115 ic
->fl
= fopen(fname
, "r");
116 if (ic
->fl
== NULL
) fatal("can't open file: '%s'", fname
);
118 ic
->fname
= strdup(fname
);
120 for (char *t
= ic
->fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
125 for (int f
= 0; f
< incLevel
; ++f
) fputc(' ', stderr
);
127 fprintf(stderr
, "compiling: %s\n", ic
->fname
);
131 static void closeFile (void) {
133 InputContext
*ic
= ictx
;
137 if (ic
->fname
) free(ic
->fname
);
142 for (int f = 0; f < incLevel-1; ++f) fputc(' ', stderr);
143 fprintf(stderr, "compiling: %s\n", ictx->fname);
150 ////////////////////////////////////////////////////////////////////////////////
151 static void ungetChar (int c
) {
152 if (c
!= EOF
&& ictx
!= NULL
) {
153 if (ictx
->ucnt
>= 4) fatal("too many unread chars");
154 ictx
->uca
[ictx
->ucnt
++] = c
;
159 static int tokenWantFileName
= 0;
161 static int nextChar (void) {
164 if (ictx
== NULL
) return EOF
;
165 if (ictx
->ucnt
> 0) {
166 c
= ictx
->uca
[--ictx
->ucnt
];
171 c
= (ictx
==NULL
? EOF
: '\n');
173 if (c
== '\n') ++(ictx
->lineno
);
176 if (!tokenWantFileName
) {
177 if (c
>= 'A' && c
<= 'Z') c
+= 32; // tolower
183 static void skipSpaces (void) {
187 if (c
== EOF
) return;
189 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
196 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
214 if (c
> 32) { ungetChar(c
); break; }
219 static int digit (int c
, int base
) {
220 if (c
== EOF
) return -1;
221 if (c
>= 'a' && c
<= 'z') c
-= 32;
222 if (c
> '9' && c
< 'A') return -1;
225 if (c
< 0 || c
>= base
) return -1;
230 static void getNumber (int c
) {
237 if (c
== EOF
) return;
240 case 'b': case 'B': base
= 2; break;
241 case 'o': case 'O': base
= 8; break;
242 case 'd': case 'D': base
= 10; break;
243 case 'x': case 'X': base
= 16; break;
245 if (isalpha(c
)) fatal("invalid number");
246 if (c
!= EOF
) ungetChar(c
);
250 if (digit(c
, base
) < 0) fatal("invalid number");
255 int d
= digit(c
, base
);
258 tint
= (tint
*base
)+d
;
259 if (tint
> 32767) fatal("number constant too big");
262 if (c
!= EOF
&& isalpha(c
)) fatal("invalid number");
263 if (c
!= EOF
) ungetChar(c
);
267 static int nextToken (void) {
270 memset(tstr
, 0, sizeof(tstr
));
274 if (ictx
== NULL
) return TK_EOF
;
276 if (c
== EOF
) return TK_EOF
;
277 if (c
>= 127) fatal("invalid char (%d)", c
);
281 if (isalpha(c
) || c
== '_' || c
== '$' || c
== '.' || c
== '@' || (tokenWantFileName
&& (c
== '/' || c
== '\\'))) {
287 if (isalnum(c
) || c
== '_' || c
== '$' || c
== '.' || c
== '@' || (tokenWantFileName
&& (c
== '/' || c
== '\\'))) {
288 if (f
>= MAX_TOKEN_LENGTH
-1) fatal("identifier too long");
289 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
292 //if (c != ';' && c > 32) fatal("invalid identifier");
297 if (!isalnum(tstr
[0]) && !tstr
[1]) {
298 if (c
!= EOF
&& c
> 32) ungetChar(c
);
304 if (!tokenWantFileName
) {
307 } else if (c
<= 32 && c
!= '\n') {
310 if (c
== ':') { token
= TK_LABELDEF
; break; }
311 if (c
> 32) ungetChar(c
);
312 if (c
== EOF
|| c
> 32) break;
315 if (c
!= EOF
&& c
> 32) ungetChar(c
);
319 for (f
= 0; vmOpNames
[f
]; ++f
) {
320 if (strcmp(tstr
, vmOpNames
[f
]) == 0) {
321 if (token
== TK_LABELDEF
) fatal("invalid label: '%s'", tstr
);
322 token
= TK_VM_OPERATOR
+f
;
326 if (token
< TK_VM_OPERATOR
) {
327 if (strcmp(tstr
, "drp") == 0) token
= VMX_DRP
;
328 else if (strcmp(tstr
, "dup") == 0) token
= VMX_DUP
;
329 else if (strcmp(tstr
, "rtn") == 0) token
= VMX_RTN
;
331 } else if (strcmp(tstr
, "return") == 0) {
335 } else if (c
== '-' || c
== '+') {
339 if ((c
= nextChar()) != EOF
) {
342 if (neg
) tint
= -tint
;
347 } else if (isdigit(c
)) {
358 ////////////////////////////////////////////////////////////////////////////////
359 typedef struct LabelRefInfo
{
360 struct LabelRefInfo
*next
;
366 typedef struct LabelInfo
{
367 struct LabelInfo
*next
;
370 int value
; // code && <0: not defined yet (forward ref)
371 int ext
; // extern (<0), public(>0), normal(0)
372 LabelRefInfo
*fixes
; // for undefined code labels
373 LabelRefInfo
*refs
; // for externals
378 static LabelInfo
*labels
= NULL
;
379 static LabelRefInfo
*relrefs
= NULL
; // info for relocaions
381 static int vglast
= 0;
382 static int vtlast
= 0;
383 static int vtloc
= VM_VARS_SIZE
-1; // local thread (var 126 is used for internal thing)
386 static LabelRefInfo
*lrefRemoveDups (LabelRefInfo
*list
) {
387 LabelRefInfo
*p
= NULL
, *c
= list
;
394 for (LabelRefInfo
*r
= list
; r
!= c
; r
= r
->next
) if (r
->pc
== c
->pc
&& r
->size
== c
->size
) { dup
= 1; break; }
399 LabelRefInfo
*n
= c
->next
;
401 if (p
!= NULL
) p
->next
= n
;
406 if (p
== NULL
) list
= c
;
415 // returns new head (created item)
416 static LabelRefInfo
*addLabelRefToList (LabelRefInfo
*list
, int pc
) {
419 res
= calloc(1, sizeof(LabelRefInfo
));
420 if (res
== NULL
) fatal("out of memory");
428 static void freeLabelRefList (LabelRefInfo
*list
) {
429 while (list
!= NULL
) {
430 LabelRefInfo
*r
= list
;
438 static void addExternRef (LabelInfo
*l
, int pc
, int size
) {
439 if (l
!= NULL
&& l
->ext
< 0) {
441 size
= (l
->type
== LB_CODE
|| l
->type
== LB_CONST
|| l
->type
== LB_SVAR
) ? 2 : 1;
443 l
->refs
= addLabelRefToList(l
->refs
, pc
);
444 l
->refs
->size
= size
;
449 static void addLabelRef (LabelInfo
*l
, int pc
) {
452 addExternRef(l
, pc
, -1);
453 if (l
->type
== LB_CODE
&& l
->ext
>= 0) {
454 // record fixup info for undefined code labels
455 if (l
->value
< 0) l
->fixes
= addLabelRefToList(l
->fixes
, pc
);
456 // record reference info for code labels
457 relrefs
= addLabelRefToList(relrefs
, pc
);
463 static void fixupLabelRefs (LabelInfo
*l
, int pc
) {
467 for (LabelRefInfo
*fix
= l
->fixes
; fix
!= NULL
; fix
= fix
->next
) {
468 vmcode
[fix
->pc
+0] = (l
->value
)&0xff;
469 vmcode
[fix
->pc
+1] = ((l
->value
)>>8)&0xff;
471 freeLabelRefList(l
->fixes
);
477 static void checkLabels (void) {
478 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
479 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
480 if (!l
->used
) { l
->used
= -1; fprintf(stderr
, "WARNING: unused %s label '%s'\n", ltnames
[l
->type
], l
->name
); }
485 static void freeLabels (void) {
488 LabelInfo
*l
= labels
;
491 freeLabelRefList(l
->fixes
);
492 freeLabelRefList(l
->refs
);
499 static void freeLocalLabels (int onlyVars
) {
500 LabelInfo
*p
= NULL
, *l
= labels
;
503 LabelInfo
*n
= l
->next
;
505 if ((!onlyVars
|| l
->type
!= LB_CODE
) && l
->name
[0] == '.') {
506 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
507 if (!l
->used
) { l
->used
= -1; fprintf(stderr
, "WARNING: unused %s label '%s'\n", ltnames
[l
->type
], l
->name
); }
508 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
509 freeLabelRefList(l
->fixes
);
510 freeLabelRefList(l
->refs
);
513 if (p
!= NULL
) p
->next
= n
; else labels
= n
;
522 static LabelInfo
*findLabel (const char *name
) {
523 if (name
&& name
[0] == '@') ++name
;
524 if (!name
|| !name
[0]) return NULL
;
525 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) if (strcmp(l
->name
, name
) == 0) return l
;
530 static LabelInfo
*addLabel (const char *name
) {
533 if (!name
|| !name
[0]) abort();
534 //if (strcmp(name, "item_glovesg") == 0) fatal("!!!");
535 if (findLabel(name
)) fatal("duplicate label: '%s'", name
);
536 l
= malloc(sizeof(LabelInfo
));
537 if (l
== NULL
) fatal("out of memory");
538 l
->name
= strdup(name
);
539 if (l
->name
== NULL
) fatal("out of memory");
551 ////////////////////////////////////////////////////////////////////////////////
553 LabelInfo
*l
; // !=NULL: label
555 int value
; // if l==NULL
556 int vartype
; // 0: global; 1: tlocal; 2: stack
560 static void setIntOperand (OperandInfo
*op
, int value
) {
568 static void setLabelOperand (OperandInfo
*op
, LabelInfo
*l
) {
576 static void getOperand (OperandInfo
*op
, int wantCode
) {
577 if (token
== TK_ID
) {
578 LabelInfo
*l
= findLabel(tstr
);
586 if (wantCode
&& l
->type
!= LB_CODE
) fatal("code offset expected");
588 setLabelOperand(op
, l
);
593 if (token
== TK_NUM
) {
594 if (wantCode
) fatal("code offset expected");
595 setIntOperand(op
, tint
);
601 //if (wantCode) fatal("code offset expected");
603 if (token
== TK_ID
) {
604 LabelInfo
*l
= findLabel(tstr
);
606 if (l
== NULL
|| l
->type
== LB_CODE
) fatal("unknown variable: '%s'", tstr
);
607 setLabelOperand(op
, l
);
608 } else if (token
== '@' || token
== '.') {
611 if (nextToken() != TK_NUM
) fatal("index expected");
612 setIntOperand(op
, tint
);
615 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
616 op
->vartype
= loc
=='@' ? 0 : 1;
621 } else if (token
== TK_NUM
) {
623 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
624 setIntOperand(op
, tint
);
627 fatal("invalid operand");
630 if (nextToken() != ']') fatal("']' expected");
635 fprintf(stderr
, "*%d [%s] %d\n", token
, tstr
, tint
);
636 fatal("invalid operand");
640 static inline int hasOperand (void) {
641 return (token
!= TK_EOF
&& token
!= TK_LABELDEF
&& token
< TK_VM_OPERATOR
);
645 ////////////////////////////////////////////////////////////////////////////////
646 static void emitByte (int b
) {
647 if (b
< 0 || b
> 255) fatal("internal error");
648 if (pc
>= 32768) fatal("code too big");
653 static void emitOpCode (int opc
, int opcount
) {
654 if (opc
< 0 || opc
> 63 || opcount
< 0 || opcount
> 3) fatal("internal error");
655 emitByte(opc
|(opcount
<<6));
660 static void emitInteger (int val) {
661 emitByte(255); // special
663 emitByte((val>>8)&0xff);
668 static void emitOperand (OperandInfo
*op
) {
669 if (!op
|| op
->var
< 0) return;
676 switch (op
->l
->type
) {
677 case LB_GVAR
: addLabelRef(op
->l
, pc
); emitByte(op
->l
->value
|0x80); break;
678 case LB_TVAR
: addLabelRef(op
->l
, pc
); emitByte(op
->l
->value
); break;
680 emitByte(127); // special
681 addLabelRef(op
->l
, pc
);
682 emitByte(op
->l
->value
&0xff);
683 emitByte((op
->l
->value
>>8)&0xff);
685 default: fatal("internal error");
689 switch (op
->vartype
) {
690 case 0: emitByte(op
->value
|0x80); break; // global
691 case 1: emitByte(op
->value
); break; // tlocal
693 emitByte(127); // special
694 emitByte(op
->value
&0xff);
695 emitByte((op
->value
>>8)&0xff);
697 default: fatal("internal error");
705 emitByte(255); // special
709 addLabelRef(op
->l
, pc
);
710 emitByte(op
->l
->value
&0xff);
711 emitByte((op
->l
->value
>>8)&0xff);
714 if (op
->value
< -32767 || op
->value
> 32767) fatal("invalid value");
715 emitByte(op
->value
&0xff);
716 emitByte((op
->value
>>8)&0xff);
722 static void emitInstruction (int opc
, OperandInfo
*op0
, OperandInfo
*op1
, OperandInfo
*op2
) {
725 if (op0
&& op0
->var
>= 0) ++ocnt
;
726 if (ocnt
== 1 && op1
&& op1
->var
>= 0) ++ocnt
;
727 if (ocnt
== 2 && op2
&& op2
->var
>= 0) ++ocnt
;
728 emitOpCode(opc
, ocnt
);
729 if (ocnt
> 0) emitOperand(op0
);
730 if (ocnt
> 1) emitOperand(op1
);
731 if (ocnt
> 2) emitOperand(op2
);
735 ////////////////////////////////////////////////////////////////////////////////
736 static void doNoOperands (int opcode
) {
737 emitInstruction(opcode
, NULL
, NULL
, NULL
);
741 static void doMath (int opcode
) {
742 OperandInfo op0
, op1
, op2
;
744 op0
.var
= op1
.var
= op2
.var
= -1;
753 if (op2
.var
!= 1) fatal("variable expected as third operand");
755 if (op0
.var
!= 1) fatal("variable expected as first operand");
759 emitInstruction(opcode
, &op0
, &op1
, &op2
);
763 static void doJXX (int opcode
) {
764 OperandInfo op0
, op1
, op2
;
766 op0
.var
= op1
.var
= op2
.var
= -1;
778 emitInstruction(opcode
, &op0
, &op1
, &op2
);
782 static void doBranch (int opcode
) {
789 emitInstruction(opcode
, &op0
, NULL
, NULL
);
793 static void doBSR (int opcode
) {
794 OperandInfo op0
, op1
, op2
;
796 op0
.var
= op1
.var
= op2
.var
= -1;
808 emitInstruction(opcode
, &op0
, &op1
, &op2
);
812 static void doMap (int opcode
, int lastMBV
) {
813 OperandInfo op0
, op1
, op2
;
815 op0
.var
= op1
.var
= op2
.var
= -1;
827 emitInstruction(opcode
, &op0
, &op1
, &op2
);
831 static void doRST (int opcode
) {
832 OperandInfo op0
, op1
, op2
;
834 op0
.var
= op1
.var
= op2
.var
= -1;
846 emitInstruction(opcode
, &op0
, &op1
, &op2
);
850 static void doSet (int opcode
) {
851 OperandInfo op0
, op1
, op2
;
853 op0
.var
= op1
.var
= op2
.var
= -1;
863 if (op2
.var
< 0 && op0
.var
!= 1) fatal("first operand should be var");
864 emitInstruction(opcode
, &op0
, &op1
, &op2
);
868 static void doDrop (int opcode
) {
872 if (hasOperand()) getOperand(&op0
, 0);
873 if (op0
.var
> 0) fatal("number expected");
874 if (op0
.value
< 0) fatal("positive number expected");
875 emitInstruction(opcode
, &op0
, NULL
, NULL
);
879 static void doPush (int opcode
) {
880 OperandInfo op0
, op1
, op2
;
882 op0
.var
= op1
.var
= op2
.var
= -1;
892 emitInstruction(opcode
, &op0
, &op1
, &op2
);
896 static void doPop (int opcode
) {
897 OperandInfo op0
, op1
, op2
;
899 op0
.var
= op1
.var
= op2
.var
= -1;
911 emitInstruction(opcode
, &op0
, &op1
, &op2
);
915 static void doSwap (int opcode
) {
916 OperandInfo op0
, op1
;
918 op0
.var
= op1
.var
= -1;
926 emitInstruction(opcode
, &op0
, &op1
, NULL
);
930 static void doDepth (int opcode
) {
934 if (hasOperand()) getOperand(&op0
, 0);
935 emitInstruction(opcode
, &op0
, NULL
, NULL
);
939 static void doPickRoll (int opcode
) {
940 OperandInfo op0
, op1
;
942 op0
.var
= op1
.var
= -1;
948 if (op1
.var
!= 1) fatal("second argument must be variable");
951 emitInstruction(opcode
, &op0
, &op1
, NULL
);
955 static void doNew (int opcode
) {
956 OperandInfo op0
, op1
;
958 op0
.var
= op1
.var
= -1;
966 emitInstruction(opcode
, &op0
, &op1
, NULL
);
970 static void doTId (int opcode
) {
974 if (hasOperand()) getOperand(&op0
, 0);
975 emitInstruction(opcode
, &op0
, NULL
, NULL
);
979 static void doKillSusRes (int opcode
) {
983 if (hasOperand()) getOperand(&op0
, 0);
984 emitInstruction(opcode
, &op0
, NULL
, NULL
);
988 static void doSta (int opcode
) {
989 OperandInfo op0
, op1
;
991 op0
.var
= op1
.var
= -1;
997 if (op1
.var
!= 1) fatal("variable expected");
1000 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1004 static void doGet (int opcode
) {
1005 OperandInfo op0
, op1
, op2
;
1007 op0
.var
= op1
.var
= op2
.var
= -1;
1008 getOperand(&op0
, 0);
1009 if (token
!= ',') fatal("at least two operands expected");
1011 getOperand(&op1
, 0);
1014 getOperand(&op2
, 0);
1015 if (op2
.var
!= 1) fatal("variable expected as third operand");
1017 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1021 static void doRXC (int opcode
) {
1022 OperandInfo op0
, op1
;
1024 op0
.var
= op1
.var
= -1;
1026 getOperand(&op0
, 0);
1029 getOperand(&op1
, 0);
1030 if (op1
.var
!= 1) fatal("variable expected as second operand");
1033 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1037 static void doWXC (int opcode
) {
1038 OperandInfo op0
, op1
;
1040 op0
.var
= op1
.var
= -1;
1042 getOperand(&op0
, 0);
1045 getOperand(&op1
, 0);
1048 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1052 static void doRet (int opcode
) {
1053 OperandInfo op0
, op1
, op2
;
1055 op0
.var
= op1
.var
= op2
.var
= -1;
1057 getOperand(&op0
, 0);
1058 if (token
!= ',') fatal("at least two operands expected");
1060 getOperand(&op1
, 0);
1063 getOperand(&op2
, 0);
1066 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1070 ////////////////////////////////////////////////////////////////////////////////
1071 static void parseInlcude (void) {
1072 static char fname
[8192], *t
;
1074 tokenWantFileName
= 1;
1075 if (nextToken() != TK_ID
) fatal("identifier expected");
1076 tokenWantFileName
= 0;
1077 strcpy(fname
, ictx
->fname
);
1079 for (t
= fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
1081 t
= strrchr(fname
, '/');
1082 if (t
!= NULL
) t
[1] = 0; else fname
[0] = 0;
1083 strcat(fname
, tstr
);
1084 if (incLevel
> 64) fatal("too many includes");
1090 static void parseVarList (int type
, int local
, int gotext
) {
1094 if (nextToken() != TK_ID
) fatal("identifier expected");
1095 if (tstr
[0] == '.' && local
<= 0) fatal("invalid variable name: '%s'", tstr
);
1096 if (tstr
[0] != '.' && local
> 0) fatal("invalid variable name: '%s'", tstr
);
1097 l
= findLabel(tstr
);
1100 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1101 if (l
->type
!= type
) fatal("can't change existing extern label type: '%s'", tstr
);
1103 fatal("duplicate variable or label: '%s'", tstr
);
1109 if (local
> 0 && vtloc
<= vtlast
) fatal("too many local vars");
1113 l
->value
= (local
>0)?(--vtloc
):(type
==LB_GVAR
?vglast
++:vtlast
++);
1115 if (local
< 0) l
->ext
= 1;
1117 if (gotext
) l
->ext
= -1;
1118 if (l
->value
> 126) fatal("too many vars");
1119 if (nextToken() != ',') break;
1124 static void parsePublics (void) {
1128 if (nextToken() != TK_ID
) fatal("identifier expected");
1129 if (tstr
[0] == '.') fatal("invalid label name: '%s'", tstr
);
1130 l
= findLabel(tstr
);
1139 if (nextToken() != ',') break;
1144 static void parseLocList (void) {
1148 if (nextToken() != TK_ID
) fatal("identifier expected");
1149 if (tstr
[0] != '.') fatal("local identifier expected instead of '%s'", tstr
);
1150 l
= findLabel(tstr
);
1151 if (l
!= NULL
) fatal("can't redefine label as local: '%s'", tstr
);
1156 if (nextToken() != '=') fatal("'=' expected");
1157 if (nextToken() != TK_NUM
) fatal("number expected");
1159 if (nextToken() != ',') break;
1164 static void parseConst (int ext
, int gotext
) {
1167 if (nextToken() != TK_ID
) fatal("identifier expected");
1168 if (tstr
[0] == '.') fatal("invalid constant name: '%s'", tstr
);
1169 l
= findLabel(tstr
);
1172 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1173 if (l
->type
!= LB_CONST
) fatal("can't change existing extern label type: '%s'", tstr
);
1175 fatal("constant must be unique: '%s'", tstr
);
1184 if (nextToken() == '=') nextToken();
1185 if (token
!= TK_NUM
) fatal("constant must be numeric");
1188 if (ext
) l
->ext
= 1;
1189 if (gotext
) l
->ext
= -1;
1194 static char procname
[8192];
1195 static int proclocals
= 0;
1196 static int procargs
= 0;
1197 static int lastWasReturn
= 0;
1200 static void parseProc (int ext
) {
1201 LabelInfo
*l
, *a
= NULL
;
1205 if (nextToken() != TK_ID
) fatal("identifier expected");
1206 if (tstr
[0] == '.') fatal("invalid proc name: '%s'", tstr
);
1207 if (procname
[0]) fatal("unclosed proc: '%s'", procname
);
1208 strcpy(procname
, tstr
);
1211 freeLocalLabels(0); // vars and code
1213 l
= findLabel(tstr
);
1215 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate proc label: '%s'", tstr
);
1216 fixupLabelRefs(l
, pc
);
1222 if (ext
) l
->ext
= 1;
1226 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "arg") == 0 || strcmp(tstr
, "args") == 0)) {
1228 if (nextToken() != TK_ID
) fatal("identifier expected");
1229 if (tstr
[0] != '.') fatal("argument name must starts with '.'");
1230 l
= findLabel(tstr
);
1231 if (l
!= NULL
) fatal("duplicate argument: '%s'", l
->name
);
1236 if (nextToken() != ',') break;
1240 // -1: return address, -2: last arg
1241 for (spt
= -2, l
= a
; l
->type
== LB_SVAR
; l
= l
->next
) {
1247 spt
= -1; // first local
1248 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "local") == 0 || strcmp(tstr
, "locals") == 0)) {
1250 if (nextToken() != TK_ID
) fatal("identifier expected");
1251 if (tstr
[0] != '.') fatal("local variable name must starts with '.'");
1252 l
= findLabel(tstr
);
1253 if (l
!= NULL
) fatal("duplicate local: '%s'", l
->name
);
1259 for (l
= a
; l
!= NULL
&& l
->type
== LB_SVAR
; l
= l
->next
) --(l
->value
);
1260 if (nextToken() != ',') break;
1264 if (proclocals
> 0) {
1268 setIntOperand(&op0
, -proclocals
);
1269 emitInstruction(VM_POP
, &op0
, NULL
, NULL
);
1274 static void parseEndP (void) {
1275 if (!procname
[0]) fatal("'endp' without 'proc'");
1276 if (nextToken() != TK_ID
) fatal("identifier expected");
1277 if (strcmp(procname
, tstr
) != 0) fatal("endp for '%s' in proc '%s'", tstr
, procname
);
1278 //if (!lastWasReturn) fatal("no 'return' in proc");
1285 static void doReturn (void) {
1286 OperandInfo op0
, op1
, op2
;
1288 if (!procname
[0]) fatal("'return' without 'proc'");
1290 op0
.var
= op1
.var
= op2
.var
= -1;
1291 if (hasOperand()) getOperand(&op2
, 0); // result
1292 setIntOperand(&op0
, proclocals
);
1293 setIntOperand(&op1
, procargs
);
1294 emitInstruction(VM_RET
, &op0
, &op1
, &op2
);
1298 static void parseLabel (int gotext
) {
1301 if (gotext
&& tstr
[0] == '.') fatal("can't declare local extern label: '%s'", tstr
);
1302 if (tstr
[0] != '.' && tstr
[0] != '@') {
1304 if (procname
[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname
);
1306 freeLocalLabels(0); // vars and code
1308 if (tstr
[0] == '@') {
1311 while (*d
++) d
[-1] = d
[0];
1314 l
= findLabel(tstr
);
1317 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1318 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
1320 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate label: '%s'", tstr
);
1321 fixupLabelRefs(l
, pc
);
1327 if (gotext
) l
->ext
= -1;
1332 static void parseDB (void) {
1335 if (token
== TK_ID
) {
1336 LabelInfo
*l
= findLabel(tstr
);
1338 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1339 if (l
->type
== LB_CODE
) fatal("bad label type: '%s'", l
->name
);
1342 addExternRef(l
, pc
, 1);
1343 } else if (token
!= TK_NUM
) {
1344 fatal("number expected");
1346 if (tint
< -128 || tint
> 255) fatal("bad value: %d", tint
);
1347 emitByte(tint
&0xff);
1348 if (nextToken() != ',') break;
1353 static void parseDW (void) {
1356 if (token
== TK_ID
) {
1357 LabelInfo
*l
= findLabel(tstr
);
1363 //fatal("unknown label: '%s'", tstr);
1366 addExternRef(l
, pc
, 2);
1368 } else if (token
!= TK_NUM
) {
1369 fatal("number expected");
1371 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1372 emitByte(tint
&0xff);
1373 emitByte((tint
>>8)&0xff);
1374 if (nextToken() != ',') break;
1380 static void parseAndPutString (int qch
) {
1382 int ch
= nextChar();
1384 if (ch
== EOF
) fatal("unterminated string");
1386 if (ch
== qch
) break;
1391 if (ch
== EOF
) fatal("invalid escape");
1393 case 'a': emitByte('\a'); break;
1394 case 'b': emitByte('\b'); break;
1395 case 'e': emitByte('\x1b'); break;
1396 case 'f': emitByte('\f'); break;
1397 case 'n': emitByte('\n'); break;
1398 case 'r': emitByte('\r'); break;
1399 case 't': emitByte('\t'); break;
1400 case 'v': emitByte('\v'); break;
1401 case '"': case '\'': case '\\': case ' ': emitByte(ch
); break;
1403 n
= digit(nextChar(), 16);
1404 if (n
< 0) fatal("invalid hex escape");
1406 if (ch
== EOF
) fatal("invalid hex escape");
1407 if (digit(ch
, 16)) {
1408 n
= n
*16+digit(ch
, 16);
1414 default: fatal("invalid escape: '%c'", ch
);
1422 if (ch
== EOF
) return;
1437 static void parseDAscii (int zeroend
) {
1443 if (ch
== EOF
) break;
1445 case '"': tokenWantFileName
= 1; parseAndPutString(ch
); tokenWantFileName
= 0; break;
1446 case '\'': tokenWantFileName
= 1; parseAndPutString(ch
); tokenWantFileName
= 0; break;
1450 if (token
== TK_ID
) {
1451 LabelInfo
*l
= findLabel(tstr
);
1453 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1454 if (l
->type
== LB_CODE
) fatal("bad label type: '%s'", l
->name
);
1456 addExternRef(l
, pc
, 1);
1458 } else if (token
!= TK_NUM
) {
1459 fatal("number expected");
1461 if (tint
< -128 || tint
> 255) fatal("bad value: %d", tint
);
1462 emitByte(tint
&0xff);
1464 if (nextToken() != ',') break;
1466 if (zeroend
) emitByte(0);
1470 static void process (void) {
1472 LabelInfo
*l
= addLabel("retval");
1475 l
->value
= VM_VARS_SIZE
-1;
1480 while (token
!= TK_EOF
) {
1483 if (token
== TK_LABELDEF
) {
1484 if (strcmp(tstr
, "extern") == 0) {
1485 if (gotext
) fatal("double 'extern'");
1490 // new label or operator
1491 if (strcmp(tstr
, "include") == 0) {
1494 } else if (strcmp(tstr
, "defloc") == 0) {
1495 if (gotext
) fatal("extern labels must not be locals");
1497 } else if (strcmp(tstr
, "defgvar") == 0) {
1498 parseVarList(LB_GVAR
, 0, gotext
);
1499 } else if (strcmp(tstr
, "defevar") == 0) { // extern global
1500 parseVarList(LB_GVAR
, -1, gotext
);
1501 } else if (strcmp(tstr
, "deftvar") == 0) {
1502 freeLocalLabels(1); // only vars
1503 vtloc
= VM_VARS_SIZE
;
1504 parseVarList(LB_TVAR
, 0, gotext
);
1505 } else if (strcmp(tstr
, "defetvar") == 0) {
1506 freeLocalLabels(1); // only vars
1507 vtloc
= VM_VARS_SIZE
;
1508 parseVarList(LB_TVAR
, -1, gotext
);
1509 } else if (strcmp(tstr
, "deflvar") == 0) {
1510 if (gotext
) fatal("extern labels must not be locals");
1511 parseVarList(LB_TVAR
, 1, gotext
);
1512 } else if (strcmp(tstr
, "public") == 0) {
1513 if (gotext
) fatal("invalid extern label declaration");
1515 } else if (strcmp(tstr
, "proc") == 0 || strcmp(tstr
, "eproc") == 0) {
1516 if (gotext
) fatal("invalid extern label declaration");
1517 parseProc(tstr
[0] == 'e');
1518 } else if (strcmp(tstr
, "endp") == 0) {
1519 if (gotext
) fatal("invalid extern label declaration");
1521 } else if (strcmp(tstr
, "const") == 0 || strcmp(tstr
, "econst") == 0) {
1522 parseConst(tstr
[0] == 'e', gotext
);
1523 } else if (strcmp(tstr
, "db") == 0) {
1524 if (gotext
) fatal("invalid extern label declaration");
1526 } else if (strcmp(tstr
, "dw") == 0) {
1527 if (gotext
) fatal("invalid extern label declaration");
1529 } else if (strcmp(tstr
, "da") == 0) {
1530 if (gotext
) fatal("invalid extern label declaration");
1532 } else if (strcmp(tstr
, "dz") == 0) {
1533 if (gotext
) fatal("invalid extern label declaration");
1544 if (token
!= TK_ID
) fatal("label declaration expected after 'extern'");
1545 if (tstr
[0] == '.') fatal("extern label can't be local: '%s'", tstr
);
1546 l
= findLabel(tstr
);
1548 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1549 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
1561 if (token
< TK_VM_OPERATOR
) fatal("mnemonics expected");
1563 if (opc
< 600) opc
-= TK_VM_OPERATOR
;
1651 if (procname
[0]) doReturn(); else doRet(opc
);
1657 doNoOperands(VM_PSH
);
1669 if (procname
[0]) fatal("'proc' without 'endp': '%s'", procname
);
1673 ////////////////////////////////////////////////////////////////////////////////
1674 static int cfWriteByte (FILE *fl
, int value
) {
1677 b
= (value
&0xff)^SECRET
;
1678 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
1683 static int cfWriteWord (FILE *fl
, int value
) {
1684 if (cfWriteByte(fl
, value
) != 0) return -1;
1685 if (cfWriteByte(fl
, value
>>8) != 0) return -1;
1690 static int cfWriteCode (FILE *fl
) {
1691 for (int f
= 0; f
< pc
; ++f
) if (cfWriteByte(fl
, vmcode
[f
]) != 0) return -1;
1696 static int cfWriteRels (FILE *fl
) {
1697 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) {
1698 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
1704 static int cfWritePublicLabels (FILE *fl
) {
1705 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
1706 if (l
->ext
>= 0 && l
->name
[0]) {
1707 int len
= strlen(l
->name
);
1709 if (cfWriteByte(fl
, l
->type
|(l
->ext
>0?0x80:0)) != 0) return -1;
1714 if (cfWriteByte(fl
, l
->value
) != 0) return -1;
1719 if (cfWriteWord(fl
, l
->value
) != 0) return -1;
1725 if (len
> 255) len
= 255;
1726 if (cfWriteByte(fl
, len
) != 0) return -1;
1727 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
1734 static int cfWriteExtLabels (FILE *fl
) {
1735 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
1736 if (l
->ext
< 0 && l
->name
[0] && l
->refs
!= NULL
) {
1737 int len
= strlen(l
->name
), rcnt
= 0;
1739 if (cfWriteByte(fl
, l
->type
) != 0) return -1;
1740 if (len
> 255) len
= 255;
1741 if (cfWriteByte(fl
, len
) != 0) return -1;
1742 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
1744 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
1745 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
1746 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) {
1747 if (cfWriteByte(fl
, r
->size
) != 0) return -1;
1748 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
1756 static int writeCodeFile (FILE *fl
) {
1757 static const char *sign
= "AVM1";
1758 int lcnt
= 0, elcnt
= 0;
1761 relrefs
= lrefRemoveDups(relrefs
);
1762 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
1763 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
1765 if (l
->ext
>= 0) ++lcnt
;
1767 l
->refs
= lrefRemoveDups(l
->refs
);
1768 if (l
->refs
!= NULL
) ++elcnt
;
1772 fprintf(stderr
, "%d bytes of code, %d public labels, %d fixups, %d externs\n", pc
, lcnt
, rcnt
, elcnt
);
1774 if (fwrite(sign
, 4, 1, fl
) != 1) return -1;
1776 if (cfWriteWord(fl
, pc
) != 0) return -1;
1778 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
1779 // number of extern labels
1780 if (cfWriteWord(fl
, elcnt
) != 0) return -1;
1782 if (cfWriteWord(fl
, lcnt
) != 0) return -1;
1784 if (cfWriteCode(fl
) != 0) return -1;
1785 if (cfWriteRels(fl
) != 0) return -1;
1786 if (cfWriteExtLabels(fl
) != 0) return -1;
1787 if (cfWritePublicLabels(fl
) != 0) return -1;
1793 ////////////////////////////////////////////////////////////////////////////////
1795 # include "cmdline.c"
1799 ////////////////////////////////////////////////////////////////////////////////
1800 int main (int argc
, char *argv
[]) {
1807 fprintf(stderr
, "usage: awasm infile outfile\n");
1811 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
1813 while (ictx
!= NULL
) closeFile();
1814 freeLocalLabels(0); // vars and code
1816 //if (argc > 3) dumpGlobalVars(argv[3]);
1818 FILE *fo
= fopen(argv
[2], "wb");
1821 if (fo
== NULL
) { fprintf(stderr
, "FATAL: can't create output file: '%s'\n", argv
[2]); return 1; }
1822 res
= writeCodeFile(fo
);
1823 if (fclose(fo
) != 0) res
= -1;
1825 fprintf(stderr
, "FATAL: error writing output file: '%s'\n", argv
[2]);
1831 freeLabelRefList(relrefs
);