2 * This program is free software: you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation, either version 3 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 /* WARNING! HERE BE DRAGONS! */
24 #include "awishcommon.h"
28 ////////////////////////////////////////////////////////////////////////////////
32 ////////////////////////////////////////////////////////////////////////////////
33 static const char *ltnames
[] = {
42 ////////////////////////////////////////////////////////////////////////////////
43 #define MAX_TOKEN_LENGTH (256)
64 ////////////////////////////////////////////////////////////////////////////////
65 static int optWarnings
= 1;
68 ////////////////////////////////////////////////////////////////////////////////
69 static uint8_t vmcode
[65536];
73 ////////////////////////////////////////////////////////////////////////////////
74 typedef struct MStrListItem
{
75 struct MStrListItem
*next
;
81 typedef struct InputContext
{
82 struct InputContext
*prev
;
89 char *text
; // not NULL: macro argument expansion
90 struct MacroDef
*macro
; // !NULL: we are inside macro expanding
91 int tpos
; // position in macro or in text
92 MStrListItem
*macnames
;
93 MStrListItem
*macargs
;
97 static InputContext
*ictx
= NULL
;
99 static char tstr
[MAX_TOKEN_LENGTH
+1];
101 static int incLevel
= 0;
104 ////////////////////////////////////////////////////////////////////////////////
105 typedef struct MacroDef
{
106 struct MacroDef
*next
;
110 MStrListItem
*argnames
;
114 static MacroDef
*macros
= NULL
;
117 static void freeMStrList (MStrListItem
*list
) {
118 while (list
!= NULL
) {
119 MStrListItem
*c
= list
;
122 if (c
->newname
!= NULL
) free(c
->newname
);
123 if (c
->macname
!= NULL
) free(c
->macname
);
129 static void freeMacroList (void) {
130 while (macros
!= NULL
) {
131 MacroDef
*mc
= macros
;
134 freeMStrList(mc
->argnames
);
135 if (mc
->name
) free(mc
->name
);
136 if (mc
->text
) free(mc
->text
);
142 ////////////////////////////////////////////////////////////////////////////////
143 static __attribute__((__noreturn__
)) __attribute__((format(printf
, 1, 2))) void fatal (const char *fmt
, ...) {
146 while (ictx
!= NULL
&& ictx
->fl
== NULL
) ictx
= ictx
->prev
;
149 fprintf(stderr
, "FATAL (line %d, file '%s'): ", ictx
->lineno
, ictx
->fname
);
151 fprintf(stderr
, "FATAL: ");
154 vfprintf(stderr
, fmt
, ap
);
156 fprintf(stderr
, "\n");
161 ////////////////////////////////////////////////////////////////////////////////
162 static void addMacroArgDef (MacroDef
*mc
, const char *name
) {
163 MStrListItem
*p
= NULL
, *c
;
165 for (MStrListItem
*ml
= mc
->argnames
; ml
!= NULL
; p
= ml
, ml
= ml
->next
) {
166 if (strcmp(ml
->macname
, name
) == 0) fatal("duplicate macro argument name: '%s'", name
);
168 if ((c
= calloc(1, sizeof(MStrListItem
))) == NULL
) fatal("out of memory");
169 if ((c
->macname
= strdup(name
)) == NULL
) fatal("out of memory");
180 static MStrListItem
*genUniqueMacName (MStrListItem
*list
, const char *macname
) {
182 static int count
= 0;
184 for (res
= list
; res
!= NULL
; res
= res
->next
) if (strcmp(res
->macname
, macname
) == 0) return list
;
185 if ((res
= calloc(1, sizeof(MStrListItem
))) == NULL
) fatal("out of memory");
186 if ((res
->macname
= strdup(macname
)) == NULL
) fatal("out of memory");
187 if ((res
->newname
= calloc(1, 128)) == NULL
) fatal("out of memory");
188 sprintf(res
->newname
, ". lmd #%d", count
++);
194 static MStrListItem
*addMacroArg (MStrListItem
*args
, const char *argname
, const char *argtext
) {
197 if ((res
= calloc(1, sizeof(MStrListItem
))) == NULL
) fatal("out of memory");
198 if ((res
->macname
= strdup(argname
)) == NULL
) fatal("out of memory");
199 if ((res
->newname
= strdup(argtext
)) == NULL
) fatal("out of memory");
205 ////////////////////////////////////////////////////////////////////////////////
206 static void openFile (const char *fname
) {
207 InputContext
*ic
= calloc(1, sizeof(InputContext
));
209 if (ic
== NULL
) fatal("out of memory");
210 ic
->fl
= fopen(fname
, "r");
211 if (ic
->fl
== NULL
) fatal("can't open file: '%s'", fname
);
213 ic
->fname
= strdup(fname
);
215 for (char *t
= ic
->fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
219 for (int f
= 0; f
< incLevel
; ++f
) fputc(' ', stderr
);
221 fprintf(stderr
, "compiling: %s\n", ic
->fname
);
225 static void openText (const char *text
) {
226 InputContext
*ic
= calloc(1, sizeof(InputContext
));
228 if (ic
== NULL
) fatal("out of memory");
231 if ((ic
->text
= strdup(text
)) == NULL
) fatal("out of memory");
236 static void openMacro (MacroDef
*mac
, MStrListItem
*args
) {
237 InputContext
*ic
= calloc(1, sizeof(InputContext
));
239 if (ic
== NULL
) fatal("out of memory");
248 static void closeFile (void) {
250 InputContext
*ic
= ictx
;
253 if (ic
->fl
!= NULL
) {
257 if (ic
->fname
) free(ic
->fname
);
258 if (ic
->text
!= NULL
) free(ic
->text
);
259 freeMStrList(ic
->macnames
);
260 freeMStrList(ic
->macargs
);
266 static inline int inText (void) {
267 return (ictx
!= NULL
&& ictx
->text
!= NULL
);
271 ////////////////////////////////////////////////////////////////////////////////
272 static int nextToken (void);
274 static void transformToken (void) {
275 // check if this is macroarg
276 if (!inText() && (token
== TK_LABELDEF
|| token
== TK_ID
)) {
277 for (InputContext
*ic
= ictx
; ic
!= NULL
; ic
= ic
->prev
) {
278 for (MStrListItem
*ml
= ic
->macargs
; ml
!= NULL
; ml
= ml
->next
) {
279 if (strcmp(tstr
, ml
->macname
) == 0) {
280 if (token
!= TK_ID
) fatal("macro argument redefinitions are prohibited");
281 //printf("marg [%s]: [%s]\n", ml->macname, ml->newname);
282 openText(ml
->newname
);
289 // check macro labels
290 if (!inText() && tstr
[0] == '$' && (token
== TK_LABELDEF
|| token
== TK_ID
)) {
291 for (InputContext
*ic
= ictx
; ic
!= NULL
; ic
= ic
->prev
) {
292 for (MStrListItem
*ml
= ic
->macnames
; ml
!= NULL
; ml
= ml
->next
) {
293 if (strcmp(tstr
, ml
->macname
) == 0) { strcpy(tstr
, ml
->newname
); return; }
297 // not found or in text; generate new macro name
298 if (!inText() && tstr
[0] == '$' && (token
== TK_LABELDEF
|| token
== TK_ID
)) {
299 for (InputContext
*ic
= ictx
; ic
!= NULL
; ic
= ic
->prev
) {
300 if (ic
->macro
!= NULL
) {
301 strcpy(tstr
, (ic
->macnames
= genUniqueMacName(ic
->macnames
, tstr
))->newname
);
309 static void ungetChar (int c
) {
311 if (ictx
->ucnt
>= 4) fatal("too many unread chars");
312 ictx
->uca
[ictx
->ucnt
++] = c
;
317 static int tokenWantFileName
= 0;
319 static int nextChar (void) {
322 if (ictx
== NULL
) return EOF
;
323 if (ictx
->ucnt
> 0) {
324 c
= ictx
->uca
[--ictx
->ucnt
];
328 } else if (ictx
->fl
!= NULL
) {
331 } else if (ictx
->text
!= NULL
) {
332 c
= ictx
->text
[ictx
->tpos
++];
333 if (c
== 0) { --(ictx
->tpos
); c
= EOF
; }
335 c
= ictx
->macro
->text
[ictx
->tpos
++];
336 if (c
== 0) { --(ictx
->tpos
); c
= EOF
; }
341 c
= (ictx
==NULL
? EOF
: '\n');
347 if (c
== '\n') ++(ictx
->lineno
);
350 if (!tokenWantFileName
) {
351 if (c
>= 'A' && c
<= 'Z') c
+= 32; // tolower
357 static void skipSpaces (int allowNL
) {
361 if (c
== EOF
) return;
362 if (c
== '\n' && !allowNL
) { ungetChar(c
); return; }
364 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
365 if (!allowNL
) { if (c
!= EOF
) ungetChar(c
); return; }
372 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
373 if (!allowNL
) { if (c
!= EOF
) ungetChar(c
); return; }
382 if (c
== '\n') wasNL
= 1;
385 if (c
== '\n') wasNL
= 1;
387 if (c
== EOF
) fatal("unterminated comment");
390 if (!allowNL
&& wasNL
) { ungetChar('\n'); return; }
397 if (c
> 32) { ungetChar(c
); break; }
402 static int digit (int c
, int base
) {
403 if (c
== EOF
) return -1;
404 if (c
>= 'a' && c
<= 'z') c
-= 32;
405 if (c
> '9' && c
< 'A') return -1;
408 if (c
< 0 || c
>= base
) return -1;
413 static void getNumber (int c
) {
420 if (c
== EOF
) return;
423 case 'b': case 'B': base
= 2; break;
424 case 'o': case 'O': base
= 8; break;
425 case 'd': case 'D': base
= 10; break;
426 case 'x': case 'X': base
= 16; break;
428 if (isalpha(c
)) fatal("invalid number");
429 if (c
!= EOF
) ungetChar(c
);
433 if (digit(c
, base
) < 0) fatal("invalid number");
438 int d
= digit(c
, base
);
441 tint
= (tint
*base
)+d
;
442 if (tint
> 32767) fatal("number constant too big");
445 if (c
!= EOF
&& isalpha(c
)) fatal("invalid number");
446 if (c
!= EOF
) ungetChar(c
);
450 static int nextToken (void) {
453 memset(tstr
, 0, sizeof(tstr
));
457 if (ictx
== NULL
) return TK_EOF
;
459 if (c
== EOF
) return TK_EOF
;
460 if (c
>= 127) fatal("invalid char (%d)", c
);
464 if (isalpha(c
) || c
== '_' || c
== '$' || c
== '.' || c
== '@' || (tokenWantFileName
&& (c
== '/' || c
== '\\'))) {
470 if (isalnum(c
) || c
== '_' || c
== '$' || c
== '.' || c
== '@' || (tokenWantFileName
&& (c
== '/' || c
== '\\'))) {
471 if (f
>= MAX_TOKEN_LENGTH
-1) fatal("identifier too long");
472 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
475 //if (c != ';' && c > 32) fatal("invalid identifier");
480 if (!isalnum(tstr
[0]) && !tstr
[1]) {
481 if (c
!= EOF
) ungetChar(c
);
487 if (!tokenWantFileName
) {
491 if (c
!= EOF
) ungetChar(c
);
495 for (f
= 0; vmOpNames
[f
]; ++f
) {
496 if (strcmp(tstr
, vmOpNames
[f
]) == 0) {
497 if (token
== TK_LABELDEF
) fatal("invalid label: '%s'", tstr
);
498 token
= TK_VM_OPERATOR
+f
;
502 if (token
< TK_VM_OPERATOR
) {
503 if (strcmp(tstr
, "drp") == 0) token
= VMX_DRP
;
504 else if (strcmp(tstr
, "dup") == 0) token
= VMX_DUP
;
505 else if (strcmp(tstr
, "rtn") == 0) token
= VMX_RTN
;
506 else if (strcmp(tstr
, "saj") == 0) token
= VMX_SAJ
;
508 } else if (strcmp(tstr
, "return") == 0) {
511 if (strcmp(tstr
, "@@") == 0) strcpy(tstr
, "."); // special label
512 transformToken(); // macro transformations
514 } else if (c
== '-' || c
== '+') {
518 if ((c
= nextChar()) != EOF
) {
521 if (neg
) tint
= -tint
;
526 } else if (isdigit(c
)) {
537 ////////////////////////////////////////////////////////////////////////////////
538 typedef struct LabelRefInfo
{
539 struct LabelRefInfo
*next
;
545 typedef struct LabelInfo
{
546 struct LabelInfo
*next
;
549 int value
; // code && <0: not defined yet (forward ref)
550 int ext
; // extern (<0), public(>0), normal(0)
551 LabelRefInfo
*fixes
; // for undefined code labels
552 LabelRefInfo
*refs
; // for externals
557 static LabelInfo
*labels
= NULL
;
558 static LabelRefInfo
*relrefs
= NULL
; // info for relocaions
559 static LabelRefInfo
*refFwd
= NULL
; // @@f references (will be resolved on next @@)
560 static int prevTmpLabelPC
= -1; // previous @@ PC (-1: not defined yet)
561 static LabelInfo labelTempBack
, labelTempFwd
;
563 static int vglast
= 0;
564 static int vtlast
= 0;
565 static int vtloc
= VM_VARS_SIZE
-1; // local thread (var 126 is used for internal thing)
568 typedef struct VarFixup
{
569 struct VarFixup
*next
;
574 static VarFixup
*gvfixes
= NULL
;
575 static VarFixup
*tvfixes
= NULL
;
577 static VarFixup
*addVarFixup (VarFixup
*list
, int pc
) {
578 VarFixup
*res
= calloc(1, sizeof(VarFixup
));
580 if (res
== NULL
) fatal("out of memory");
587 static void addGVarFixup (int pc
) {
588 gvfixes
= addVarFixup(gvfixes
, pc
);
592 static void addTVarFixup (int pc
) {
593 tvfixes
= addVarFixup(tvfixes
, pc
);
597 static LabelRefInfo
*lrefRemoveDups (LabelRefInfo
*list
) {
598 LabelRefInfo
*p
= NULL
, *c
= list
;
605 for (LabelRefInfo
*r
= list
; r
!= c
; r
= r
->next
) if (r
->pc
== c
->pc
&& r
->size
== c
->size
) { dup
= 1; break; }
610 LabelRefInfo
*n
= c
->next
;
612 if (p
!= NULL
) p
->next
= n
;
617 if (p
== NULL
) list
= c
;
626 // returns new head (created item)
627 static LabelRefInfo
*addLabelRefToList (LabelRefInfo
*list
, int pc
) {
630 res
= calloc(1, sizeof(LabelRefInfo
));
631 if (res
== NULL
) fatal("out of memory");
639 static void freeLabelRefList (LabelRefInfo
*list
) {
640 while (list
!= NULL
) {
641 LabelRefInfo
*r
= list
;
649 static void addExternRef (LabelInfo
*l
, int pc
, int size
) {
650 if (l
== &labelTempBack
|| l
== &labelTempFwd
) return;
651 if (l
!= NULL
&& l
->ext
< 0) {
653 size
= (l
->type
== LB_CODE
|| l
->type
== LB_CONST
|| l
->type
== LB_SVAR
) ? 2 : 1;
655 l
->refs
= addLabelRefToList(l
->refs
, pc
);
656 l
->refs
->size
= size
;
661 static void addLabelRef (LabelInfo
*l
, int pc
) {
662 if (l
== &labelTempBack
) {
663 if (prevTmpLabelPC
< 0) fatal("no backward '@@' defined");
664 //fprintf(stderr, "backref to '@@'\n");
665 vmcode
[pc
+0] = prevTmpLabelPC
&0xff;
666 vmcode
[pc
+1] = (prevTmpLabelPC
>>8)&0xff;
667 relrefs
= addLabelRefToList(relrefs
, pc
);
670 if (l
== &labelTempFwd
) {
671 //fprintf(stderr, "fwdref to '@@'\n");
672 refFwd
= addLabelRefToList(refFwd
, pc
);
673 relrefs
= addLabelRefToList(relrefs
, pc
);
678 addExternRef(l
, pc
, -1);
679 if (l
->type
== LB_CODE
&& l
->ext
>= 0) {
680 // record fixup info for undefined code labels
681 if (l
->value
< 0) l
->fixes
= addLabelRefToList(l
->fixes
, pc
);
682 // record reference info for code labels
683 relrefs
= addLabelRefToList(relrefs
, pc
);
689 static void fixupFwdTmpRefs (void) {
690 for (LabelRefInfo
*fix
= refFwd
; fix
!= NULL
; fix
= fix
->next
) {
691 vmcode
[fix
->pc
+0] = pc
&0xff;
692 vmcode
[fix
->pc
+1] = (pc
>>8)&0xff;
694 freeLabelRefList(refFwd
);
699 static void fixupLabelRefs (LabelInfo
*l
, int pc
) {
700 if (l
== &labelTempBack
|| l
== &labelTempFwd
) return;
704 for (LabelRefInfo
*fix
= l
->fixes
; fix
!= NULL
; fix
= fix
->next
) {
705 vmcode
[fix
->pc
+0] = (l
->value
)&0xff;
706 vmcode
[fix
->pc
+1] = ((l
->value
)>>8)&0xff;
708 freeLabelRefList(l
->fixes
);
714 static void checkLabels (void) {
715 if (refFwd
!= NULL
) fatal("unresolved forward references to '@@' found");
716 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
717 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
720 if (optWarnings
&& l
->type
!= LB_CONST
&& strcmp(l
->name
, "retval") != 0 && l
->ext
== 0) fprintf(stderr
, "WARNING: unused %s label '%s'\n", ltnames
[l
->type
], l
->name
);
726 static void freeLabels (void) {
729 LabelInfo
*l
= labels
;
732 freeLabelRefList(l
->fixes
);
733 freeLabelRefList(l
->refs
);
740 static void freeLocalLabels (int onlyVars
) {
741 LabelInfo
*p
= NULL
, *l
= labels
;
743 if (refFwd
!= NULL
) fatal("unresolved references to '@@' found");
746 LabelInfo
*n
= l
->next
;
748 if ((!onlyVars
|| l
->type
!= LB_CODE
) && l
->name
[0] == '.') {
749 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
752 if (optWarnings
&& l
->type
!= LB_CONST
&& strcmp(l
->name
, "retval") != 0 && l
->ext
== 0) fprintf(stderr
, "WARNING: unused %s label '%s'\n", ltnames
[l
->type
], l
->name
);
754 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
755 freeLabelRefList(l
->fixes
);
756 freeLabelRefList(l
->refs
);
759 if (p
!= NULL
) p
->next
= n
; else labels
= n
;
768 static AWISH_PURE LabelInfo
*findLabel (const char *name
) {
769 if (name
&& strcmp(name
, "@@b") == 0) return &labelTempBack
;
770 if (name
&& strcmp(name
, "@@f") == 0) return &labelTempFwd
;
771 if (name
&& strcmp(name
, ".") == 0) {
772 //fprintf(stderr, "LOOKUP: '@@'\n");
775 if (name
&& name
[0] == '@') ++name
;
776 if (!name
|| !name
[0]) return NULL
;
777 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) if (strcmp(l
->name
, name
) == 0) return l
;
782 static LabelInfo
*addLabel (const char *name
) {
785 if (!name
|| !name
[0]) fatal("internal error: empty label name");
786 if (strcmp(name
, "@@b") == 0 || strcmp(name
, "@@f") == 0 || strcmp(name
, ".") == 0) fatal("can't define special label: '%s'", name
);
787 if (findLabel(name
)) fatal("duplicate label: '%s'", name
);
788 l
= calloc(1, sizeof(LabelInfo
));
789 if (l
== NULL
) fatal("out of memory");
790 l
->name
= strdup(name
);
791 if (l
->name
== NULL
) fatal("out of memory");
803 static void newTempLabel (void) {
806 //fprintf(stderr, "new '@@'\n");
810 ////////////////////////////////////////////////////////////////////////////////
811 typedef struct OperandInfo
{
812 LabelInfo
*l
; // !=NULL: label
814 int value
; // if l==NULL
815 int vartype
; // 0: global; 1: tlocal; 2: stack
816 struct OperandInfo
*next
;
820 static int stackAdj
= 0;
823 static void setIntOperand (OperandInfo
*op
, int value
) {
831 static void setLabelOperand (OperandInfo
*op
, LabelInfo
*l
) {
839 static void getOperand (OperandInfo
*op
, int wantCode
) {
840 if (token
== TK_ID
) {
841 LabelInfo
*l
= findLabel(tstr
);
849 if (wantCode
&& l
->type
!= LB_CODE
) fatal("code offset expected");
851 setLabelOperand(op
, l
);
856 if (token
== TK_NUM
) {
857 if (wantCode
) fatal("code offset expected");
858 setIntOperand(op
, tint
);
864 //if (wantCode) fatal("code offset expected");
866 if (token
== TK_ID
) {
867 LabelInfo
*l
= findLabel(tstr
);
869 if (l
== NULL
|| l
->type
== LB_CODE
) fatal("unknown variable: '%s'", tstr
);
870 setLabelOperand(op
, l
);
871 } else if (token
== '@' || token
== '.') {
874 if (nextToken() != TK_NUM
) fatal("index expected");
875 setIntOperand(op
, tint
);
878 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
879 op
->vartype
= loc
=='@' ? 0 : 1;
884 } else if (token
== TK_NUM
) {
886 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
887 setIntOperand(op
, tint
);
890 fatal("invalid operand");
893 if (nextToken() != ']') fatal("']' expected");
898 fprintf(stderr
, "*%d [%s] %d\n", token
, tstr
, tint
);
899 fatal("invalid operand");
903 static inline int hasOperand (void) {
904 return (token
!= TK_EOF
&& token
!= TK_LABELDEF
&& token
< TK_VM_OPERATOR
);
908 ////////////////////////////////////////////////////////////////////////////////
909 static void emitByte (int b
) {
910 if (b
< 0 || b
> 255) fatal("internal error");
911 if (pc
>= 32768) fatal("code too big");
916 static void emitOpCode (int opc
, int opcount
) {
917 if (opc
< 0 || opc
> 63 || opcount
< 0 || opcount
> 3) fatal("internal error");
918 emitByte(opc
|(opcount
<<6));
923 static void emitInteger (int val) {
924 emitByte(255); // special
926 emitByte((val>>8)&0xff);
931 static void emitOperand (OperandInfo
*op
) {
932 if (!op
|| op
->var
< 0) return;
939 switch (op
->l
->type
) {
941 emitByte(op
->l
->value
|0x80);
942 addLabelRef(op
->l
, pc
-1);
943 if (op
->l
->ext
>= 0 && op
->l
->name
[0] != '.') addGVarFixup(pc
-1);
946 emitByte(op
->l
->value
);
947 addLabelRef(op
->l
, pc
-1);
948 if (op
->l
->ext
>= 0 && op
->l
->name
[0] != '.') addTVarFixup(pc
-1);
951 emitByte(127); // special
952 if (op
->l
->value
< 0) {
953 emitByte((op
->l
->value
-stackAdj
)&0xff);
954 emitByte(((op
->l
->value
-stackAdj
)>>8)&0xff);
956 emitByte(op
->l
->value
&0xff);
957 emitByte((op
->l
->value
>>8)&0xff);
959 addLabelRef(op
->l
, pc
-2);
961 default: fatal("internal error");
965 switch (op
->vartype
) {
966 case 0: emitByte(op
->value
|0x80); break; // global
967 case 1: emitByte(op
->value
); break; // tlocal
969 emitByte(127); // special
971 emitByte((op
->value
-stackAdj
)&0xff);
972 emitByte(((op
->value
-stackAdj
)>>8)&0xff);
974 emitByte(op
->value
&0xff);
975 emitByte((op
->value
>>8)&0xff);
978 default: fatal("internal error");
986 emitByte(255); // special
990 emitByte(op
->l
->value
&0xff);
991 emitByte((op
->l
->value
>>8)&0xff);
992 addLabelRef(op
->l
, pc
-2);
995 if (op
->value
< -32767 || op
->value
> 32767) fatal("invalid value");
996 emitByte(op
->value
&0xff);
997 emitByte((op
->value
>>8)&0xff);
1003 static void emitInstruction (int opc
, OperandInfo
*op0
, OperandInfo
*op1
, OperandInfo
*op2
) {
1006 if (op0
&& op0
->var
>= 0) ++ocnt
;
1007 if (ocnt
== 1 && op1
&& op1
->var
>= 0) ++ocnt
;
1008 if (ocnt
== 2 && op2
&& op2
->var
>= 0) ++ocnt
;
1009 emitOpCode(opc
, ocnt
);
1010 if (ocnt
> 0) emitOperand(op0
);
1011 if (ocnt
> 1) emitOperand(op1
);
1012 if (ocnt
> 2) emitOperand(op2
);
1016 ////////////////////////////////////////////////////////////////////////////////
1017 static void doNoOperands (int opcode
) {
1018 emitInstruction(opcode
, NULL
, NULL
, NULL
);
1022 static void doMath (int opcode
) {
1023 OperandInfo op0
, op1
, op2
;
1025 op0
.var
= op1
.var
= op2
.var
= -1;
1027 getOperand(&op0
, 0);
1030 getOperand(&op1
, 0);
1033 getOperand(&op2
, 0);
1034 if (op2
.var
!= 1) fatal("variable expected as third operand");
1036 if (op0
.var
!= 1) fatal("variable expected as first operand");
1040 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1044 static void doJXX (int opcode
) {
1045 OperandInfo op0
, op1
, op2
;
1047 op0
.var
= op1
.var
= op2
.var
= -1;
1049 getOperand(&op0
, 1);
1052 getOperand(&op1
, 0);
1055 getOperand(&op2
, 0);
1059 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1063 static void doBranch (int opcode
) {
1068 getOperand(&op0
, 1);
1070 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1074 static void doBSR (int opcode
) {
1075 OperandInfo op0
, *op1
= NULL
, *op2
= NULL
, *olist
= NULL
, *c
= NULL
;
1076 int osadj
= stackAdj
, ocnt
= 0;;
1080 getOperand(&op0
, (opcode
== VM_BSR
) ? 1 : 0);
1081 // collect other operands
1082 while (token
== ',') {
1083 OperandInfo
*o
= calloc(sizeof(OperandInfo
), 1);
1085 if (o
== NULL
) fatal("out of memory");
1088 if (olist
!= NULL
) c
->next
= o
; else olist
= o
;
1106 while (olist
!= NULL
) {
1107 OperandInfo
*o
= olist
;
1109 if (o
->next
->next
== NULL
) {
1117 emitInstruction(VM_PSH
, o
, NULL
, NULL
);
1122 emitInstruction(opcode
, &op0
, op1
, op2
);
1123 if (op2
!= NULL
) free(op2
);
1124 if (op1
!= NULL
) free(op1
);
1129 static void doMap (int opcode
, int lastMBV
) {
1130 OperandInfo op0
, op1
, op2
;
1132 op0
.var
= op1
.var
= op2
.var
= -1;
1134 getOperand(&op0
, 0);
1137 getOperand(&op1
, 0);
1140 getOperand(&op2
, 0);
1144 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1148 static void doRST (int opcode
) {
1153 static void doSet (int opcode
) {
1154 OperandInfo op0
, op1
, op2
;
1156 op0
.var
= op1
.var
= op2
.var
= -1;
1157 getOperand(&op0
, 0);
1160 getOperand(&op1
, 0);
1163 getOperand(&op2
, 0);
1166 if (op2
.var
< 0 && op0
.var
!= 1) fatal("first operand should be var");
1167 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1171 static void doDrop (int opcode
) {
1175 if (hasOperand()) getOperand(&op0
, 0);
1176 if (op0
.var
> 0) fatal("number expected");
1177 if (op0
.value
< 0) fatal("positive number expected");
1178 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1182 static void doPush (int opcode
) {
1183 OperandInfo op0
, op1
, op2
;
1185 op0
.var
= op1
.var
= op2
.var
= -1;
1186 getOperand(&op0
, 0);
1189 getOperand(&op1
, 0);
1192 getOperand(&op2
, 0);
1195 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1199 static void doPop (int opcode
) {
1200 OperandInfo op0
, op1
, op2
;
1202 op0
.var
= op1
.var
= op2
.var
= -1;
1204 getOperand(&op0
, 0);
1207 getOperand(&op1
, 0);
1210 getOperand(&op2
, 0);
1214 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1218 static void doSwap (int opcode
) {
1219 OperandInfo op0
, op1
;
1221 op0
.var
= op1
.var
= -1;
1223 getOperand(&op0
, 0);
1226 getOperand(&op1
, 0);
1229 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1233 static void doDepth (int opcode
) {
1237 if (hasOperand()) getOperand(&op0
, 0);
1238 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1242 static void doPickRoll (int opcode
) {
1243 OperandInfo op0
, op1
;
1245 op0
.var
= op1
.var
= -1;
1247 getOperand(&op0
, 0);
1250 getOperand(&op1
, 0);
1251 if (op1
.var
!= 1) fatal("second argument must be variable");
1254 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1258 static void doNew (int opcode
) {
1259 OperandInfo op0
, op1
;
1261 op0
.var
= op1
.var
= -1;
1263 getOperand(&op0
, 0);
1266 getOperand(&op1
, 0);
1269 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1273 static void doTId (int opcode
) {
1277 if (hasOperand()) getOperand(&op0
, 0);
1278 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1282 static void doKillSusRes (int opcode
) {
1286 if (hasOperand()) getOperand(&op0
, 0);
1287 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1291 static void doSta (int opcode
) {
1292 OperandInfo op0
, op1
;
1294 op0
.var
= op1
.var
= -1;
1296 getOperand(&op0
, 0);
1299 getOperand(&op1
, 0);
1300 if (op1
.var
!= 1) fatal("variable expected");
1303 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1307 static void doGet (int opcode
) {
1308 OperandInfo op0
, op1
, op2
;
1310 op0
.var
= op1
.var
= op2
.var
= -1;
1311 getOperand(&op0
, 0);
1312 if (token
!= ',') fatal("at least two operands expected");
1314 getOperand(&op1
, 0);
1317 getOperand(&op2
, 0);
1318 if (op2
.var
!= 1) fatal("variable expected as third operand");
1320 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1324 static void doRXC (int opcode
) {
1325 OperandInfo op0
, op1
;
1327 op0
.var
= op1
.var
= -1;
1329 getOperand(&op0
, 0);
1332 getOperand(&op1
, 0);
1333 if (op1
.var
!= 1) fatal("variable expected as second operand");
1336 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1340 static void doWXC (int opcode
) {
1341 OperandInfo op0
, op1
;
1343 op0
.var
= op1
.var
= -1;
1345 getOperand(&op0
, 0);
1348 getOperand(&op1
, 0);
1351 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1355 static void doRet (int opcode
) {
1356 OperandInfo op0
, op1
, op2
;
1358 op0
.var
= op1
.var
= op2
.var
= -1;
1360 getOperand(&op0
, 0);
1361 if (token
!= ',') fatal("at least two operands expected");
1363 getOperand(&op1
, 0);
1366 getOperand(&op2
, 0);
1369 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1373 static void doSAj (int opcode
) {
1374 if (token
!= TK_NUM
) fatal("number expected");
1380 ////////////////////////////////////////////////////////////////////////////////
1381 static void parseInlcude (void) {
1382 static char fname
[8192], *t
;
1384 tokenWantFileName
= 1;
1385 if (nextToken() != TK_ID
) fatal("identifier expected");
1386 tokenWantFileName
= 0;
1387 strcpy(fname
, ictx
->fname
);
1389 for (t
= fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
1391 t
= strrchr(fname
, '/');
1392 if (t
!= NULL
) t
[1] = 0; else fname
[0] = 0;
1393 strcat(fname
, tstr
);
1394 if (incLevel
> 64) fatal("too many includes");
1400 static void parseVarList (int type
, int local
, int gotext
) {
1404 if (nextToken() != TK_ID
) fatal("identifier expected");
1405 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1406 if (tstr
[0] == '.' && local
<= 0) fatal("invalid variable name: '%s'", tstr
);
1407 if (tstr
[0] != '.' && local
> 0) fatal("invalid variable name: '%s'", tstr
);
1408 l
= findLabel(tstr
);
1411 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1412 if (l
->type
!= type
) fatal("can't change existing extern label type: '%s'", tstr
);
1414 fatal("duplicate variable or label: '%s'", tstr
);
1420 if (local
> 0 && vtloc
<= vtlast
) fatal("too many local vars");
1424 l
->value
= (local
>0)?(--vtloc
):(type
==LB_GVAR
?vglast
++:vtlast
++);
1426 if (local
< 0) l
->ext
= 1;
1428 if (gotext
) l
->ext
= -1;
1429 if (l
->value
> 126) fatal("too many vars");
1430 if (nextToken() != ',') break;
1435 static void parseUsedVarList (void) {
1439 if (nextToken() != TK_ID
) fatal("identifier expected");
1440 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1441 l
= findLabel(tstr
);
1442 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1444 if (nextToken() != ',') break;
1449 static void parseLocList (void) {
1453 if (nextToken() != TK_ID
) fatal("identifier expected");
1454 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1455 if (tstr
[0] != '.') fatal("local identifier expected instead of '%s'", tstr
);
1456 l
= findLabel(tstr
);
1457 if (l
!= NULL
) fatal("can't redefine label as local: '%s'", tstr
);
1462 if (nextToken() != '=') fatal("'=' expected");
1463 if (nextToken() != TK_NUM
) fatal("number expected");
1465 if (nextToken() != ',') break;
1470 static void parseConst (int ext
, int gotext
) {
1473 if (nextToken() != TK_ID
) fatal("identifier expected");
1474 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1475 if (tstr
[0] == '.') fatal("invalid constant name: '%s'", tstr
);
1476 l
= findLabel(tstr
);
1479 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1480 if (l
->type
!= LB_CONST
) fatal("can't change existing extern label type: '%s'", tstr
);
1482 fatal("constant must be unique: '%s'", tstr
);
1491 if (nextToken() == '=') nextToken();
1492 if (token
!= TK_NUM
) fatal("constant must be numeric");
1495 if (ext
) l
->ext
= 1;
1496 if (gotext
) l
->ext
= -1;
1501 static char procname
[8192];
1502 static int proclocals
= 0;
1503 static int procargs
= 0;
1504 static int lastWasReturn
= 0;
1507 static void parseProc (int ext
) {
1508 LabelInfo
*l
, *a
= NULL
;
1512 if (nextToken() != TK_ID
) fatal("identifier expected");
1513 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1514 if (tstr
[0] == '.') fatal("invalid proc name: '%s'", tstr
);
1515 if (procname
[0]) fatal("unclosed proc: '%s'", procname
);
1516 strcpy(procname
, tstr
);
1519 freeLocalLabels(0); // vars and code
1521 l
= findLabel(tstr
);
1523 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate proc label: '%s'", tstr
);
1524 fixupLabelRefs(l
, pc
);
1530 if (ext
) l
->ext
= 1;
1534 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "arg") == 0 || strcmp(tstr
, "args") == 0)) {
1536 if (nextToken() != TK_ID
) fatal("identifier expected");
1537 if (tstr
[0] != '.') fatal("argument name must starts with '.'");
1538 l
= findLabel(tstr
);
1539 if (l
!= NULL
) fatal("duplicate argument: '%s'", l
->name
);
1544 if (nextToken() != ',') break;
1548 // -1: return address, -2: last arg
1549 for (spt
= -2, l
= a
; l
->type
== LB_SVAR
; l
= l
->next
) {
1555 spt
= -1; // first local
1556 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "local") == 0 || strcmp(tstr
, "locals") == 0)) {
1558 if (nextToken() != TK_ID
) fatal("identifier expected");
1559 if (tstr
[0] != '.') fatal("local variable name must starts with '.'");
1560 l
= findLabel(tstr
);
1561 if (l
!= NULL
) fatal("duplicate local: '%s'", l
->name
);
1567 for (l
= a
; l
!= NULL
&& l
->type
== LB_SVAR
; l
= l
->next
) --(l
->value
);
1568 if (nextToken() != ',') break;
1572 if (proclocals
> 0) {
1576 setIntOperand(&op0
, -proclocals
);
1577 emitInstruction(VM_POP
, &op0
, NULL
, NULL
);
1582 static void parseEndP (void) {
1583 if (!procname
[0]) fatal("'endp' without 'proc'");
1584 if (nextToken() != TK_ID
) fatal("identifier expected");
1585 if (strcmp(procname
, tstr
) != 0) fatal("endp for '%s' in proc '%s'", tstr
, procname
);
1586 //if (!lastWasReturn) fatal("no 'return' in proc");
1593 static void doReturn (void) {
1594 OperandInfo op0
, op1
, op2
;
1596 if (!procname
[0]) fatal("'return' without 'proc'");
1598 op0
.var
= op1
.var
= op2
.var
= -1;
1599 if (hasOperand()) getOperand(&op2
, 0); // result
1600 setIntOperand(&op0
, proclocals
);
1601 setIntOperand(&op1
, procargs
);
1602 emitInstruction(VM_RET
, &op0
, &op1
, &op2
);
1606 static void parseLabel (int gotext
) {
1609 if (gotext
&& tstr
[0] == '.') fatal("can't declare local extern label: '%s'", tstr
);
1610 if (strcmp(tstr
, ".") == 0) {
1615 if (tstr
[0] != '.' && tstr
[0] != '@') {
1617 if (procname
[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname
);
1619 freeLocalLabels(0); // vars and code
1621 if (tstr
[0] == '@') {
1624 while (*d
++) d
[-1] = d
[0];
1627 l
= findLabel(tstr
);
1630 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1631 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
1633 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate label: '%s'", tstr
);
1634 fixupLabelRefs(l
, pc
);
1640 if (gotext
) l
->ext
= -1;
1645 static void parseDW (void) {
1647 LabelInfo
*l
= NULL
;
1649 if (token
== TK_ID
) {
1650 l
= findLabel(tstr
);
1656 //fatal("unknown label: '%s'", tstr);
1659 } else if (token
!= TK_NUM
) {
1660 fatal("number expected");
1662 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1663 emitByte(tint
&0xff);
1664 emitByte((tint
>>8)&0xff);
1666 addLabelRef(l
, pc
-2);
1667 addExternRef(l
, pc
-2, 2);
1668 if (l
->ext
>= 0 && l
->name
[0] != '.') {
1670 case LB_GVAR
: addGVarFixup(pc
-2); break;
1671 case LB_TVAR
: addTVarFixup(pc
-2); break;
1675 if (nextToken() != ',') break;
1681 static void parseAndPutString (int qch
) {
1683 int ch
= nextChar();
1685 //printf("[%c] [%c]\n", ch, qch);
1686 if (ch
== EOF
) fatal("unterminated string");
1688 if (ch
== qch
) break;
1693 if (ch
== EOF
) fatal("invalid escape");
1695 case 'a': emitByte('\a'); break;
1696 case 'b': emitByte('\b'); break;
1697 case 'e': emitByte('\x1b'); break;
1698 case 'f': emitByte('\f'); break;
1699 case 'n': emitByte('\n'); break;
1700 case 'r': emitByte('\r'); break;
1701 case 't': emitByte('\t'); break;
1702 case 'v': emitByte('\v'); break;
1703 case '"': case '\'': case '\\': case ' ': emitByte(ch
); break;
1705 n
= digit(nextChar(), 16);
1706 if (n
< 0) fatal("invalid hex escape");
1708 if (ch
== EOF
) fatal("invalid hex escape");
1709 if (digit(ch
, 16) >= 0) {
1710 n
= n
*16+digit(ch
, 16);
1716 default: fatal("invalid escape: '%c'", ch
);
1724 if (ch
== EOF
) return;
1739 static void parseDAscii (int zeroend
) {
1744 if (token
== TK_EOF
) break;
1746 case '"': tokenWantFileName
= 1; parseAndPutString(token
); tokenWantFileName
= 0; break;
1747 case '\'': tokenWantFileName
= 1; parseAndPutString(token
); tokenWantFileName
= 0; break;
1749 l
= findLabel(tstr
);
1751 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1752 if (l
->type
== LB_CODE
) fatal("bad label type: '%s'", l
->name
);
1754 addExternRef(l
, pc
, 1);
1755 if (l
->ext
>= 0 && l
->name
[0] != '.') {
1757 case LB_GVAR
: addGVarFixup(pc
); break;
1758 case LB_TVAR
: addTVarFixup(pc
); break;
1764 if (tint
< -128 || tint
> 255) fatal("bad value: %d", tint
);
1765 emitByte(tint
&0xff);
1768 fatal("number expected");
1770 if (nextToken() != ',') break;
1772 if (zeroend
) emitByte(0);
1776 static void parsePublics (void) {
1781 if (token
== TK_LABELDEF
) {
1785 if (token
!= TK_ID
) fatal("identifier expected");
1786 if (tstr
[0] == '.') fatal("invalid label name: '%s'", tstr
);
1787 l
= findLabel(tstr
);
1796 if (nextToken() != ',') break;
1801 static void parseMacroDef (void) {
1805 int tpos
= 0, tsize
= 0;
1807 void addChar (int c
) {
1808 if (tpos
+1 > tsize
) {
1809 int newsz
= tsize
+1024;
1810 char *nn
= realloc(text
, newsz
);
1812 if (nn
== NULL
) fatal("out of memory");
1819 if (nextToken() != TK_ID
) fatal("macro name expected");
1820 if (tstr
[0] == '.') fatal("macros can't be local: '%s'", tstr
);
1821 for (mc
= macros
; mc
!= NULL
; mc
= mc
->next
) if (strcmp(mc
->name
, tstr
) == 0) fatal("macros can't be redefined: '%s'", tstr
);
1822 if ((mc
= calloc(1, sizeof(MacroDef
))) == NULL
) fatal("out of memory");
1824 if ((mc
->name
= strdup(tstr
)) == NULL
) fatal("out of memory");
1826 // now parse macro args
1829 skipSpaces(0); // no newlines allowed here
1835 if (nextToken() != TK_ID
) fatal("invalid macro argument definition");
1836 if (tstr
[0] == '.') fatal("macro argument name must not start with dot: '%s'", tstr
);
1837 addMacroArgDef(mc
, tstr
);
1840 if (c
!= ',') { ungetChar(c
); break; }
1843 // now parse macro text
1849 case EOF
: fatal("incomplete macro definition: '%s'", mc
->name
);
1850 case '\n': // check for macro end
1852 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1853 if (c
!= 'e') { ungetChar(c
); break; }
1854 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1855 if (c
!= 'n') { ungetChar(c
); addChar('e'); break; }
1856 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1857 if (c
!= 'd') { ungetChar(c
); addChar('e'); addChar('n'); break; }
1858 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1859 if (c
!= 'm') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); break; }
1860 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1861 if (c
!= 'a') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); break; }
1862 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1863 if (c
!= 'c') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); break; }
1864 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1865 if (c
!= 'r') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); break; }
1866 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1867 if (c
!= 'o') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); break; }
1869 if (c
== EOF
) goto macro_complete
;
1870 if (!isspace(c
) && c
!= ';' && c
!= '/') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); addChar('o'); break; }
1872 skipSpaces(0); // no EOLs
1874 if (c
!= '\n') fatal("invalid endmacro: '%s' (%d)", mc
->name
, c
);
1875 goto macro_complete
;
1880 tokenWantFileName
= 1;
1883 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1885 if (c
== '"') break;
1888 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1892 tokenWantFileName
= 0;
1894 case '\'': // string
1896 tokenWantFileName
= 1;
1899 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1903 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1904 if (c
!= '\'') { ungetChar(c
); break; }
1908 tokenWantFileName
= 0;
1910 case ';': // comment
1915 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1916 if (c
== '\n') break;
1921 if (c1
== '/') goto one_line_comment
; // comment
1923 // multiline comment
1926 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1929 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1930 if (c
== '/') break;
1933 } else if (c1
== EOF
) {
1934 fatal("incomplete macro definition: '%s'", mc
->name
);
1952 static void parseMacroInvocation (MacroDef
*mc
) {
1953 MStrListItem
*args
= NULL
;
1955 if (mc
->argnames
!= NULL
) {
1956 nextToken(); // skip macro name
1957 // collect macro arguments
1958 for (MStrListItem
*a
= mc
->argnames
; a
!= NULL
; a
= a
->next
) {
1962 case TK_EOF
: fatal("macro argument expected: '%s'", mc
->name
);
1963 case TK_LABELDEF
: fatal("macro argument can't be label: '%s'", mc
->name
);
1964 case TK_NUM
: sprintf(tstr
, "%d", tint
); break;
1965 case TK_ID
: break; // ok
1968 tstr
[pos
++] = token
;
1969 tokenWantFileName
= 1;
1972 if (c
== EOF
) fatal("incomplete macro argument: '%s'", mc
->name
);
1973 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1975 if (c
== '"') break;
1978 if (c
== EOF
) fatal("incomplete macro argument: '%s'", mc
->name
);
1979 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1983 tokenWantFileName
= 0;
1986 case '\'': // string
1988 tstr
[pos
++] = token
;
1989 tokenWantFileName
= 1;
1992 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1993 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1997 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1998 if (c
!= '\'') { ungetChar(c
); break; }
1999 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
2003 tokenWantFileName
= 0;
2007 if (token
>= TK_VM_OPERATOR
) break;
2009 tstr
[pos
++] = token
;
2012 if (c
== EOF
) break;//fatal("incomplete macro definition: '%s'", mc->name);
2013 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
2014 //TODO: better comment parsing
2015 if (c
== ',' || c
== ';' || c
== '/' || c
== '\n') { ungetChar(c
); break; }
2018 while (isspace(tstr
[pos
-1])) --pos
;
2021 //fatal("invalid macro argument: '%s'", mc->name);
2023 //fprintf(stderr, "arg [%s]: [%s]\n", mc->argnames->macname, tstr);
2024 args
= addMacroArg(args
, mc
->argnames
->macname
, tstr
);
2025 if (a
->next
!= NULL
) {
2026 if (nextToken() != ',') fatal("macro argument expected: '%s'", mc
->name
);
2030 // arguments collected
2031 openMacro(mc
, args
);
2036 static void parseLabelDef () {
2039 if (nextToken() != TK_ID
) fatal("label name expected");
2041 l
= findLabel(tstr
);
2042 if (strcmp(tstr
, ".") == 0) fatal("can't declare temp label");
2046 if (l
->ext
< 0) fatal("can't declare extern label: '%s'", tstr
);
2047 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("can't redeclare label: '%s'", tstr
);
2048 //fixupLabelRefs(l, pc);
2055 if (nextToken() == '=') {
2057 //fprintf(stderr, "token=%d\n", token);
2061 //TODO: allow simple repeated math (with labels too)
2063 skipSpaces(0); // not newlines
2065 if (ch
== '-' || ch
== '+') {
2067 if (token
!= TK_NUM
) fatal("number expected");
2068 if (ch
== '-') tint
= -tint
;
2070 //fprintf(stderr, "pc=%d; tint=%d; value=%d\n", pc, tint, l->value);
2072 if (ch
!= EOF
&& ch
!= '\n') fatal("invalid label definition: '%s'", l
->name
);
2074 } else if (token
== TK_NUM
) {
2077 fatal("invalid label definition: '%s'", l
->name
);
2083 if (l
->value
< 0) l
->value
&= 0xffff;
2084 fixupLabelRefs(l
, l
->value
);
2088 static void process (void) {
2090 LabelInfo
*l
= addLabel("retval");
2093 l
->value
= VM_VARS_SIZE
-1;
2095 memset(&labelTempBack
, 0, sizeof(LabelInfo
));
2096 memset(&labelTempFwd
, 0, sizeof(labelTempFwd
));
2098 labelTempBack
.type
= labelTempFwd
.type
= LB_CODE
;
2099 labelTempBack
.value
= labelTempFwd
.value
= -1;
2104 while (token
!= TK_EOF
) {
2107 if (token
== TK_LABELDEF
) {
2108 if (strcmp(tstr
, "extern") == 0) {
2109 if (gotext
) fatal("double 'extern'");
2114 // new label or operator
2115 if (strcmp(tstr
, "include") == 0) {
2118 } else if (strcmp(tstr
, "defloc") == 0) {
2119 if (gotext
) fatal("extern labels must not be locals");
2121 } else if (strcmp(tstr
, "defgvar") == 0) {
2122 parseVarList(LB_GVAR
, 0, gotext
);
2123 } else if (strcmp(tstr
, "defevar") == 0) { // extern global
2124 parseVarList(LB_GVAR
, -1, gotext
);
2125 } else if (strcmp(tstr
, "deftvar") == 0) {
2126 freeLocalLabels(1); // only vars
2127 vtloc
= VM_VARS_SIZE
;
2128 parseVarList(LB_TVAR
, 0, gotext
);
2129 } else if (strcmp(tstr
, "defetvar") == 0) {
2130 freeLocalLabels(1); // only vars
2131 vtloc
= VM_VARS_SIZE
;
2132 parseVarList(LB_TVAR
, -1, gotext
);
2133 } else if (strcmp(tstr
, "deflvar") == 0) {
2134 if (gotext
) fatal("extern labels must not be locals");
2135 parseVarList(LB_TVAR
, 1, gotext
);
2136 } else if (strcmp(tstr
, "public") == 0) {
2137 if (gotext
) fatal("invalid extern label declaration");
2139 } else if (strcmp(tstr
, "proc") == 0 || strcmp(tstr
, "eproc") == 0) {
2140 if (gotext
) fatal("invalid extern label declaration");
2141 parseProc(tstr
[0] == 'e');
2142 } else if (strcmp(tstr
, "endp") == 0) {
2143 if (gotext
) fatal("invalid extern label declaration");
2145 } else if (strcmp(tstr
, "const") == 0 || strcmp(tstr
, "econst") == 0) {
2146 parseConst(tstr
[0] == 'e', gotext
);
2147 } else if (strcmp(tstr
, "db") == 0) {
2148 if (gotext
) fatal("invalid extern label declaration");
2150 } else if (strcmp(tstr
, "dw") == 0) {
2151 if (gotext
) fatal("invalid extern label declaration");
2153 } else if (strcmp(tstr
, "da") == 0) {
2154 if (gotext
) fatal("invalid extern label declaration");
2156 } else if (strcmp(tstr
, "dz") == 0) {
2157 if (gotext
) fatal("invalid extern label declaration");
2159 } else if (strcmp(tstr
, "macro") == 0) {
2160 if (gotext
) fatal("macros can't be external");
2162 } else if (strcmp(tstr
, "used") == 0) {
2163 if (gotext
) fatal("'used' labels can't be external");
2165 } else if (strcmp(tstr
, "gvarbase") == 0) {
2166 if (gotext
) fatal("'gvarbase' can't be external");
2167 if (nextToken() != TK_NUM
) fatal("gvarbase: number expected");
2170 } else if (strcmp(tstr
, "tvarbase") == 0) {
2171 if (gotext
) fatal("'tvarbase' can't be external");
2172 if (nextToken() != TK_NUM
) fatal("tvarbase: number expected");
2175 } else if (strcmp(tstr
, "label") == 0) {
2176 if (gotext
) fatal("'label' can't be external");
2187 if (token
!= TK_ID
) fatal("label declaration expected after 'extern'");
2188 if (tstr
[0] == '.') fatal("extern label can't be local: '%s'", tstr
);
2189 l
= findLabel(tstr
);
2191 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
2192 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
2203 // check for macro invocation
2204 if (token
== TK_ID
) {
2205 for (MacroDef
*mc
= macros
; mc
!= NULL
; mc
= mc
->next
) {
2206 if (strcmp(mc
->name
, tstr
) == 0) {
2207 parseMacroInvocation(mc
);
2213 if (token
< TK_VM_OPERATOR
) {
2214 //fprintf(stderr, "%d: [%s]\n", token, tstr);
2215 fatal("mnemonics expected");
2218 if (opc
< 600) opc
-= TK_VM_OPERATOR
;
2306 if (procname
[0]) doReturn(); else doRet(opc
);
2312 doNoOperands(VM_PSH
);
2327 if (procname
[0]) fatal("'proc' without 'endp': '%s'", procname
);
2331 ////////////////////////////////////////////////////////////////////////////////
2332 static int cfWriteByte (FILE *fl
, int value
) {
2335 b
= (value
&0xff)^SECRET
;
2336 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
2341 static int cfWriteWord (FILE *fl
, int value
) {
2342 if (cfWriteByte(fl
, value
) != 0) return -1;
2343 if (cfWriteByte(fl
, value
>>8) != 0) return -1;
2348 static int cfWriteCode (FILE *fl
) {
2349 for (int f
= 0; f
< pc
; ++f
) if (cfWriteByte(fl
, vmcode
[f
]) != 0) return -1;
2354 static int cfWriteRels (FILE *fl
) {
2355 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) {
2356 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
2362 static int cfWritePublicLabels (FILE *fl
) {
2363 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2364 if (l
->ext
>= 0 && l
->name
[0]) {
2365 int len
= strlen(l
->name
);
2367 if (cfWriteByte(fl
, l
->type
|(l
->ext
>0?0x80:0)) != 0) return -1;
2372 if (cfWriteByte(fl
, l
->value
) != 0) return -1;
2377 if (cfWriteWord(fl
, l
->value
) != 0) return -1;
2383 if (len
> 255) len
= 255;
2384 if (cfWriteByte(fl
, len
) != 0) return -1;
2385 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
2392 static int cfWriteExtLabels (FILE *fl
) {
2393 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2394 if (l
->ext
< 0 && l
->name
[0] && l
->refs
!= NULL
) {
2395 int len
= strlen(l
->name
), rcnt
= 0;
2397 if (cfWriteByte(fl
, l
->type
) != 0) return -1;
2398 if (len
> 255) len
= 255;
2399 if (cfWriteByte(fl
, len
) != 0) return -1;
2400 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
2402 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
2403 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
2404 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) {
2405 if (cfWriteByte(fl
, r
->size
) != 0) return -1;
2406 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
2414 static int cfWriteVarFixups (FILE *fl
, const VarFixup
*list
) {
2417 for (const VarFixup
*f
= list
; f
!= NULL
; f
= f
->next
) ++cnt
;
2418 if (cfWriteWord(fl
, cnt
) != 0) return -1;
2419 for (const VarFixup
*f
= list
; f
!= NULL
; f
= f
->next
) {
2420 if (cfWriteWord(fl
, f
->pc
) != 0) return -1;
2426 static int writeCodeFile (FILE *fl
) {
2427 static const char *sign
= "AVM2";
2428 int lcnt
= 0, elcnt
= 0;
2431 relrefs
= lrefRemoveDups(relrefs
);
2432 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
2433 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2435 if (l
->ext
>= 0) ++lcnt
;
2437 l
->refs
= lrefRemoveDups(l
->refs
);
2438 if (l
->refs
!= NULL
) ++elcnt
;
2442 fprintf(stderr
, "%d bytes of code, %d public labels, %d fixups, %d externs; maxgvar: %d, maxtvar: %d\n", pc
, lcnt
, rcnt
, elcnt
, vglast
, vtlast
);
2444 if (fwrite(sign
, 4, 1, fl
) != 1) return -1;
2446 if (cfWriteWord(fl
, pc
) != 0) return -1;
2448 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
2449 // number of extern labels
2450 if (cfWriteWord(fl
, elcnt
) != 0) return -1;
2452 if (cfWriteWord(fl
, lcnt
) != 0) return -1;
2454 if (cfWriteWord(fl
, vglast
) != 0) return -1;
2455 // last used thread local
2456 if (cfWriteWord(fl
, vtlast
) != 0) return -1;
2458 if (cfWriteCode(fl
) != 0) return -1;
2459 if (cfWriteRels(fl
) != 0) return -1;
2460 if (cfWriteExtLabels(fl
) != 0) return -1;
2461 if (cfWritePublicLabels(fl
) != 0) return -1;
2462 if (cfWriteVarFixups(fl
, gvfixes
) != 0) return -1;
2463 if (cfWriteVarFixups(fl
, tvfixes
) != 0) return -1;
2469 ////////////////////////////////////////////////////////////////////////////////
2471 # include "cmdline.c"
2475 ////////////////////////////////////////////////////////////////////////////////
2476 #define PUSH_BACK(_c) (*ress)[dpos++] = (_c)
2477 #define DECODE_TUPLE(tuple,bytes) \
2478 for (tmp = bytes; tmp > 0; tmp--, tuple = (tuple & 0x00ffffff)<<8) \
2479 PUSH_BACK((char)((tuple >> 24)&0xff))
2481 // returns ress length
2482 static int ascii85Decode (char **ress
, const char *srcs
/*, int start, int length*/) {
2483 static uint32_t pow85
[5] = { 85*85*85*85UL, 85*85*85UL, 85*85UL, 85UL, 1UL };
2484 const uint8_t *data
= (const uint8_t *)srcs
;
2485 int len
= strlen(srcs
);
2487 int count
= 0, c
= 0;
2489 int start
= 0, length
= len
;
2492 if (start
< 0) start
= 0; else { len
-= start
; data
+= start
; }
2493 if (length
< 0 || len
< length
) length
= len
;
2496 int xlen = 4*((length+4)/5);
2497 kstringReserve(ress, xlen);
2501 *ress
= (char *)calloc(1, len
+1);
2502 for (int f
= length
; f
> 0; --f
, ++data
) {
2504 if (c
<= ' ') continue; // skip blanks
2506 case 'z': // zero tuple
2508 //xdlog("%s: z inside ascii85 5-tuple\n", file);
2518 case '~': // '~>': end of sequence
2519 if (f
< 1 || data
[1] != '>') { free(*ress
); return -2; } // error
2520 if (count
> 0) { f
= -1; break; }
2522 if (c
< '!' || c
> 'u') {
2523 //xdlog("%s: bad character in ascii85 region: %#o\n", file, c);
2527 tuple
+= ((uint8_t)(c
-'!'))*pow85
[count
++];
2529 DECODE_TUPLE(tuple
, 4);
2536 // write last (possibly incomplete) tuple
2538 tuple
+= pow85
[count
];
2539 DECODE_TUPLE(tuple
, count
);
2548 static void decodeBA (char *str
, int len
) {
2551 for (int f
= 0; f
< len
; ++f
, ++str
) {
2561 static void printEC (const char *txt
) {
2565 if ((len
= ascii85Decode(&dest
, txt
)) >= 0) {
2566 decodeBA(dest
, len
);
2567 fprintf(stderr
, "%s\n", dest
);
2573 static int isStr85Equ (const char *txt
, const char *str
) {
2577 if ((len
= ascii85Decode(&dest
, txt
)) >= 0) {
2578 res
= (strcmp(dest
+1, str
) == 0); // +1 to ignore '/'
2585 static int checkEGG (const char *str
) {
2586 if (isStr85Equ("06:]JASq", str
) || isStr85Equ("0/i", str
)) {
2588 "H8lZV&6)1>+AZ>m)Cf8;A1/cP+CnS)0OJ`X.QVcHA4^cc5r3=m1c%0D3&c263d?EV6@4&>"
2589 "3DYQo;c-FcO+UJ;MOJ$TAYO@/FI]+B?C.L$>%:oPAmh:4Au)>AAU/H;ZakL2I!*!%J;(AK"
2590 "NIR#5TXgZ6c'F1%^kml.JW5W8e;ql0V3fQUNfKpng6ppMf&ip-VOX@=jKl;#q\"DJ-_>jG"
2591 "8#L;nm]!q;7c+hR6p;tVY#J8P$aTTK%c-OT?)<00,+q*8f&ff9a/+sbU,:`<H*[fk0o]7k"
2592 "^l6nRkngc6Tl2Ngs!!P2I%KHG=7n*an'bsgn>!*8s7TLTC+^\\\"W+<=9^%Ol$1A1eR*Be"
2593 "gqjEag:M0OnrC4FBY5@QZ&'HYYZ#EHs8t4$5]!22QoJ3`;-&=\\DteO$d6FBqT0E@:iu?N"
2594 "a5ePUf^_uEEcjTDKfMpX/9]DFL8N-Ee;*8C5'WgbGortZuh1\\N0;/rJB6'(MSmYiS\"6+"
2595 "<NK)KDV3e+Ad[@).W:%.dd'0h=!QUhghQaNNotIZGrpHr-YfEuUpsKW<^@qlZcdTDA!=?W"
2596 "Yd+-^`'G8Or)<0-T&CT.i+:mJp(+/M/nLaVb#5$p2jR2<rl7\"XlngcN`mf,[4oK5JLr\\"
2597 "m=X'(ue;'*1ik&/@T4*=j5t=<&/e/Q+2=((h`>>uN(#>&#i>2/ajK+=eib1coVe3'D)*75"
2598 "m_h;28^M6p6*D854Jj<C^,Q8Wd\"O<)&L/=C$lUAQNN<=eTD:A6kn-=EItXSss.tAS&!;F"
2599 "EsgpJTHIYNNnh'`kmX^[`*ELOHGcWbfPOT`J]A8P`=)AS;rYlR$\"-.RG440lK5:Dg?G'2"
2600 "['dE=nEm1:k,,Se_=%-6Z*L^J[)EC"
2604 if (isStr85Equ("04Jj?B)", str
)) {
2606 "IPaSa(`c:T,o9Bq3\\)IY++?+!-S9%P0/OkjE&f$l.OmK'Ai2;ZHn[<,6od7^8;)po:HaP"
2607 "m<'+&DRS:/1L7)IA7?WI$8WKTUB2tXg>Zb$.?\"@AIAu;)6B;2_PB5M?oBPDC.F)606Z$V"
2608 "=ONd6/5P*LoWKTLQ,d@&;+Ru,\\ESY*rg!l1XrhpJ:\"WKWdOg?l;=RHE:uU9C?aotBqj]"
2609 "=k8cZ`rp\"ZO=GjkfD#o]Z\\=6^]+Gf&-UFthT*hN"
2613 if (isStr85Equ("04o69A7Tr", str
)) {
2615 "Ag7d[&R#Ma9GVV5,S(D;De<T_+W).?,%4n+3cK=%4+0VN@6d\")E].np7l?8gF#cWF7SS_m"
2616 "4@V\\nQ;h!WPD2h#@\\RY&G\\LKL=eTP<V-]U)BN^b.DffHkTPnFcCN4B;]8FCqI!p1@H*_"
2617 "jHJ<%g']RG*MLqCrbP*XbNL=4D1R[;I(c*<FuesbWmSCF1jTW+rplg;9[S[7eDVl6YsjT"
2625 ////////////////////////////////////////////////////////////////////////////////
2626 int main (int argc
, char *argv
[]) {
2633 for (int f
= 1; f
< argc
; ++f
) {
2634 if (strcmp(argv
[f
], "-Wno") == 0) {
2636 for (int c
= f
+1; c
< argc
; ++c
) argv
[c
-1] = argv
[c
];
2637 argv
[--argc
] = NULL
;
2641 if (checkEGG(argv
[f
]+1)) exit(1);
2646 fprintf(stderr
, "usage: awasm infile outfile\n");
2650 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
2652 while (ictx
!= NULL
) closeFile();
2653 freeLocalLabels(0); // vars and code
2655 //if (argc > 3) dumpGlobalVars(argv[3]);
2657 FILE *fo
= fopen(argv
[2], "wb");
2660 if (fo
== NULL
) { fprintf(stderr
, "FATAL: can't create output file: '%s'\n", argv
[2]); return 1; }
2661 res
= writeCodeFile(fo
);
2662 if (fclose(fo
) != 0) res
= -1;
2664 fprintf(stderr
, "FATAL: error writing output file: '%s'\n", argv
[2]);
2670 freeLabelRefList(relrefs
);