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! */
27 ////////////////////////////////////////////////////////////////////////////////
31 ////////////////////////////////////////////////////////////////////////////////
32 static const char *ltnames
[] = {
41 ////////////////////////////////////////////////////////////////////////////////
42 #define MAX_TOKEN_LENGTH (256)
62 ////////////////////////////////////////////////////////////////////////////////
63 static int optWarnings
= 1;
66 ////////////////////////////////////////////////////////////////////////////////
67 static uint8_t vmcode
[65536];
71 ////////////////////////////////////////////////////////////////////////////////
72 typedef struct MStrListItem
{
73 struct MStrListItem
*next
;
79 typedef struct InputContext
{
80 struct InputContext
*prev
;
87 char *text
; // not NULL: macro argument expansion
88 struct MacroDef
*macro
; // !NULL: we are inside macro expanding
89 int tpos
; // position in macro or in text
90 MStrListItem
*macnames
;
91 MStrListItem
*macargs
;
95 static InputContext
*ictx
= NULL
;
97 static char tstr
[MAX_TOKEN_LENGTH
+1];
99 static int incLevel
= 0;
102 ////////////////////////////////////////////////////////////////////////////////
103 typedef struct MacroDef
{
104 struct MacroDef
*next
;
108 MStrListItem
*argnames
;
112 static MacroDef
*macros
= NULL
;
115 static void freeMStrList (MStrListItem
*list
) {
116 while (list
!= NULL
) {
117 MStrListItem
*c
= list
;
120 if (c
->newname
!= NULL
) free(c
->newname
);
121 if (c
->macname
!= NULL
) free(c
->macname
);
127 static void freeMacroList (void) {
128 while (macros
!= NULL
) {
129 MacroDef
*mc
= macros
;
132 freeMStrList(mc
->argnames
);
133 if (mc
->name
) free(mc
->name
);
134 if (mc
->text
) free(mc
->text
);
140 ////////////////////////////////////////////////////////////////////////////////
141 static __attribute__((__noreturn__
)) __attribute__((format(printf
, 1, 2))) void fatal (const char *fmt
, ...) {
144 while (ictx
!= NULL
&& ictx
->fl
== NULL
) ictx
= ictx
->prev
;
147 fprintf(stderr
, "FATAL (line %d, file '%s'): ", ictx
->lineno
, ictx
->fname
);
149 fprintf(stderr
, "FATAL: ");
152 vfprintf(stderr
, fmt
, ap
);
154 fprintf(stderr
, "\n");
159 ////////////////////////////////////////////////////////////////////////////////
160 static void addMacroArgDef (MacroDef
*mc
, const char *name
) {
161 MStrListItem
*p
= NULL
, *c
;
163 for (MStrListItem
*ml
= mc
->argnames
; ml
!= NULL
; p
= ml
, ml
= ml
->next
) {
164 if (strcmp(ml
->macname
, name
) == 0) fatal("duplicate macro argument name: '%s'", name
);
166 if ((c
= calloc(1, sizeof(MStrListItem
))) == NULL
) fatal("out of memory");
167 if ((c
->macname
= strdup(name
)) == NULL
) fatal("out of memory");
178 static MStrListItem
*genUniqueMacName (MStrListItem
*list
, const char *macname
) {
180 static int count
= 0;
182 for (res
= list
; res
!= NULL
; res
= res
->next
) if (strcmp(res
->macname
, macname
) == 0) return list
;
183 if ((res
= calloc(1, sizeof(MStrListItem
))) == NULL
) fatal("out of memory");
184 if ((res
->macname
= strdup(macname
)) == NULL
) fatal("out of memory");
185 if ((res
->newname
= calloc(1, 128)) == NULL
) fatal("out of memory");
186 sprintf(res
->newname
, ". lmd #%d", count
++);
192 static MStrListItem
*addMacroArg (MStrListItem
*args
, const char *argname
, const char *argtext
) {
195 if ((res
= calloc(1, sizeof(MStrListItem
))) == NULL
) fatal("out of memory");
196 if ((res
->macname
= strdup(argname
)) == NULL
) fatal("out of memory");
197 if ((res
->newname
= strdup(argtext
)) == NULL
) fatal("out of memory");
203 ////////////////////////////////////////////////////////////////////////////////
204 static void openFile (const char *fname
) {
205 InputContext
*ic
= calloc(1, sizeof(InputContext
));
207 if (ic
== NULL
) fatal("out of memory");
208 ic
->fl
= fopen(fname
, "r");
209 if (ic
->fl
== NULL
) fatal("can't open file: '%s'", fname
);
211 ic
->fname
= strdup(fname
);
213 for (char *t
= ic
->fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
217 for (int f
= 0; f
< incLevel
; ++f
) fputc(' ', stderr
);
219 fprintf(stderr
, "compiling: %s\n", ic
->fname
);
223 static void openText (const char *text
) {
224 InputContext
*ic
= calloc(1, sizeof(InputContext
));
226 if (ic
== NULL
) fatal("out of memory");
229 if ((ic
->text
= strdup(text
)) == NULL
) fatal("out of memory");
234 static void openMacro (MacroDef
*mac
, MStrListItem
*args
) {
235 InputContext
*ic
= calloc(1, sizeof(InputContext
));
237 if (ic
== NULL
) fatal("out of memory");
246 static void closeFile (void) {
248 InputContext
*ic
= ictx
;
251 if (ic
->fl
!= NULL
) {
255 if (ic
->fname
) free(ic
->fname
);
256 if (ic
->text
!= NULL
) free(ic
->text
);
257 freeMStrList(ic
->macnames
);
258 freeMStrList(ic
->macargs
);
264 static inline int inText (void) {
265 return (ictx
!= NULL
&& ictx
->text
!= NULL
);
269 ////////////////////////////////////////////////////////////////////////////////
270 static int nextToken (void);
272 static void transformToken (void) {
273 // check if this is macroarg
274 if (!inText() && (token
== TK_LABELDEF
|| token
== TK_ID
)) {
275 for (InputContext
*ic
= ictx
; ic
!= NULL
; ic
= ic
->prev
) {
276 for (MStrListItem
*ml
= ic
->macargs
; ml
!= NULL
; ml
= ml
->next
) {
277 if (strcmp(tstr
, ml
->macname
) == 0) {
278 if (token
!= TK_ID
) fatal("macro argument redefinitions are prohibited");
279 //printf("marg [%s]: [%s]\n", ml->macname, ml->newname);
280 openText(ml
->newname
);
287 // check macro labels
288 if (!inText() && tstr
[0] == '$' && (token
== TK_LABELDEF
|| token
== TK_ID
)) {
289 for (InputContext
*ic
= ictx
; ic
!= NULL
; ic
= ic
->prev
) {
290 for (MStrListItem
*ml
= ic
->macnames
; ml
!= NULL
; ml
= ml
->next
) {
291 if (strcmp(tstr
, ml
->macname
) == 0) { strcpy(tstr
, ml
->newname
); return; }
295 // not found or in text; generate new macro name
296 if (!inText() && tstr
[0] == '$' && (token
== TK_LABELDEF
|| token
== TK_ID
)) {
297 for (InputContext
*ic
= ictx
; ic
!= NULL
; ic
= ic
->prev
) {
298 if (ic
->macro
!= NULL
) {
299 strcpy(tstr
, (ic
->macnames
= genUniqueMacName(ic
->macnames
, tstr
))->newname
);
307 static void ungetChar (int c
) {
309 if (ictx
->ucnt
>= 4) fatal("too many unread chars");
310 ictx
->uca
[ictx
->ucnt
++] = c
;
315 static int tokenWantFileName
= 0;
317 static int nextChar (void) {
320 if (ictx
== NULL
) return EOF
;
321 if (ictx
->ucnt
> 0) {
322 c
= ictx
->uca
[--ictx
->ucnt
];
326 } else if (ictx
->fl
!= NULL
) {
329 } else if (ictx
->text
!= NULL
) {
330 c
= ictx
->text
[ictx
->tpos
++];
331 if (c
== 0) { --(ictx
->tpos
); c
= EOF
; }
333 c
= ictx
->macro
->text
[ictx
->tpos
++];
334 if (c
== 0) { --(ictx
->tpos
); c
= EOF
; }
339 c
= (ictx
==NULL
? EOF
: '\n');
345 if (c
== '\n') ++(ictx
->lineno
);
348 if (!tokenWantFileName
) {
349 if (c
>= 'A' && c
<= 'Z') c
+= 32; // tolower
355 static void skipSpaces (int allowNL
) {
359 if (c
== EOF
) return;
360 if (c
== '\n' && !allowNL
) { ungetChar(c
); return; }
362 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
363 if (!allowNL
) { if (c
!= EOF
) ungetChar(c
); return; }
370 do { c
= nextChar(); } while (c
!= EOF
&& c
!= '\n');
371 if (!allowNL
) { if (c
!= EOF
) ungetChar(c
); return; }
380 if (c
== '\n') wasNL
= 1;
383 if (c
== '\n') wasNL
= 1;
385 if (c
== EOF
) fatal("unterminated comment");
388 if (!allowNL
&& wasNL
) { ungetChar('\n'); return; }
395 if (c
> 32) { ungetChar(c
); break; }
400 static int digit (int c
, int base
) {
401 if (c
== EOF
) return -1;
402 if (c
>= 'a' && c
<= 'z') c
-= 32;
403 if (c
> '9' && c
< 'A') return -1;
406 if (c
< 0 || c
>= base
) return -1;
411 static void getNumber (int c
) {
418 if (c
== EOF
) return;
421 case 'b': case 'B': base
= 2; break;
422 case 'o': case 'O': base
= 8; break;
423 case 'd': case 'D': base
= 10; break;
424 case 'x': case 'X': base
= 16; break;
426 if (isalpha(c
)) fatal("invalid number");
427 if (c
!= EOF
) ungetChar(c
);
431 if (digit(c
, base
) < 0) fatal("invalid number");
436 int d
= digit(c
, base
);
439 tint
= (tint
*base
)+d
;
440 if (tint
> 32767) fatal("number constant too big");
443 if (c
!= EOF
&& isalpha(c
)) fatal("invalid number");
444 if (c
!= EOF
) ungetChar(c
);
448 static int nextToken (void) {
451 memset(tstr
, 0, sizeof(tstr
));
455 if (ictx
== NULL
) return TK_EOF
;
457 if (c
== EOF
) return TK_EOF
;
458 if (c
>= 127) fatal("invalid char (%d)", c
);
462 if (isalpha(c
) || c
== '_' || c
== '$' || c
== '.' || c
== '@' || (tokenWantFileName
&& (c
== '/' || c
== '\\'))) {
468 if (isalnum(c
) || c
== '_' || c
== '$' || c
== '.' || c
== '@' || (tokenWantFileName
&& (c
== '/' || c
== '\\'))) {
469 if (f
>= MAX_TOKEN_LENGTH
-1) fatal("identifier too long");
470 //if (c >= 'A' && c <= 'Z') c += 32; // tolower
473 //if (c != ';' && c > 32) fatal("invalid identifier");
478 if (!isalnum(tstr
[0]) && !tstr
[1]) {
479 if (c
!= EOF
) ungetChar(c
);
485 if (!tokenWantFileName
) {
489 if (c
!= EOF
) ungetChar(c
);
493 for (f
= 0; vmOpNames
[f
]; ++f
) {
494 if (strcmp(tstr
, vmOpNames
[f
]) == 0) {
495 if (token
== TK_LABELDEF
) fatal("invalid label: '%s'", tstr
);
496 token
= TK_VM_OPERATOR
+f
;
500 if (token
< TK_VM_OPERATOR
) {
501 if (strcmp(tstr
, "drp") == 0) token
= VMX_DRP
;
502 else if (strcmp(tstr
, "dup") == 0) token
= VMX_DUP
;
503 else if (strcmp(tstr
, "rtn") == 0) token
= VMX_RTN
;
505 } else if (strcmp(tstr
, "return") == 0) {
508 if (strcmp(tstr
, "@@") == 0) strcpy(tstr
, "."); // special label
509 transformToken(); // macro transformations
511 } else if (c
== '-' || c
== '+') {
515 if ((c
= nextChar()) != EOF
) {
518 if (neg
) tint
= -tint
;
523 } else if (isdigit(c
)) {
534 ////////////////////////////////////////////////////////////////////////////////
535 typedef struct LabelRefInfo
{
536 struct LabelRefInfo
*next
;
542 typedef struct LabelInfo
{
543 struct LabelInfo
*next
;
546 int value
; // code && <0: not defined yet (forward ref)
547 int ext
; // extern (<0), public(>0), normal(0)
548 LabelRefInfo
*fixes
; // for undefined code labels
549 LabelRefInfo
*refs
; // for externals
554 static LabelInfo
*labels
= NULL
;
555 static LabelRefInfo
*relrefs
= NULL
; // info for relocaions
556 static LabelRefInfo
*refFwd
= NULL
; // @@f references (will be resolved on next @@)
557 static int prevTmpLabelPC
= -1; // previous @@ PC (-1: not defined yet)
558 static LabelInfo labelTempBack
, labelTempFwd
;
560 static int vglast
= 0;
561 static int vtlast
= 0;
562 static int vtloc
= VM_VARS_SIZE
-1; // local thread (var 126 is used for internal thing)
565 typedef struct VarFixup
{
566 struct VarFixup
*next
;
571 static VarFixup
*gvfixes
= NULL
;
572 static VarFixup
*tvfixes
= NULL
;
575 static VarFixup
*addVarFixup (VarFixup
*list
, int pc
) {
576 VarFixup
*res
= calloc(1, sizeof(VarFixup
));
578 if (res
== NULL
) fatal("out of memory");
585 static void addGVarFixup (int pc
) {
586 gvfixes
= addVarFixup(gvfixes
, pc
);
590 static void addTVarFixup (int pc
) {
591 tvfixes
= addVarFixup(tvfixes
, pc
);
595 static LabelRefInfo
*lrefRemoveDups (LabelRefInfo
*list
) {
596 LabelRefInfo
*p
= NULL
, *c
= list
;
603 for (LabelRefInfo
*r
= list
; r
!= c
; r
= r
->next
) if (r
->pc
== c
->pc
&& r
->size
== c
->size
) { dup
= 1; break; }
608 LabelRefInfo
*n
= c
->next
;
610 if (p
!= NULL
) p
->next
= n
;
615 if (p
== NULL
) list
= c
;
624 // returns new head (created item)
625 static LabelRefInfo
*addLabelRefToList (LabelRefInfo
*list
, int pc
) {
628 res
= calloc(1, sizeof(LabelRefInfo
));
629 if (res
== NULL
) fatal("out of memory");
637 static void freeLabelRefList (LabelRefInfo
*list
) {
638 while (list
!= NULL
) {
639 LabelRefInfo
*r
= list
;
647 static void addExternRef (LabelInfo
*l
, int pc
, int size
) {
648 if (l
== &labelTempBack
|| l
== &labelTempFwd
) return;
649 if (l
!= NULL
&& l
->ext
< 0) {
651 size
= (l
->type
== LB_CODE
|| l
->type
== LB_CONST
|| l
->type
== LB_SVAR
) ? 2 : 1;
653 l
->refs
= addLabelRefToList(l
->refs
, pc
);
654 l
->refs
->size
= size
;
659 static void addLabelRef (LabelInfo
*l
, int pc
) {
660 if (l
== &labelTempBack
) {
661 if (prevTmpLabelPC
< 0) fatal("no backward '@@' defined");
662 //fprintf(stderr, "backref to '@@'\n");
663 vmcode
[pc
+0] = prevTmpLabelPC
&0xff;
664 vmcode
[pc
+1] = (prevTmpLabelPC
>>8)&0xff;
665 relrefs
= addLabelRefToList(relrefs
, pc
);
668 if (l
== &labelTempFwd
) {
669 //fprintf(stderr, "fwdref to '@@'\n");
670 refFwd
= addLabelRefToList(refFwd
, pc
);
671 relrefs
= addLabelRefToList(relrefs
, pc
);
676 addExternRef(l
, pc
, -1);
677 if (l
->type
== LB_CODE
&& l
->ext
>= 0) {
678 // record fixup info for undefined code labels
679 if (l
->value
< 0) l
->fixes
= addLabelRefToList(l
->fixes
, pc
);
680 // record reference info for code labels
681 relrefs
= addLabelRefToList(relrefs
, pc
);
687 static void fixupFwdTmpRefs (void) {
688 for (LabelRefInfo
*fix
= refFwd
; fix
!= NULL
; fix
= fix
->next
) {
689 vmcode
[fix
->pc
+0] = pc
&0xff;
690 vmcode
[fix
->pc
+1] = (pc
>>8)&0xff;
692 freeLabelRefList(refFwd
);
697 static void fixupLabelRefs (LabelInfo
*l
, int pc
) {
698 if (l
== &labelTempBack
|| l
== &labelTempFwd
) return;
702 for (LabelRefInfo
*fix
= l
->fixes
; fix
!= NULL
; fix
= fix
->next
) {
703 vmcode
[fix
->pc
+0] = (l
->value
)&0xff;
704 vmcode
[fix
->pc
+1] = ((l
->value
)>>8)&0xff;
706 freeLabelRefList(l
->fixes
);
712 static void checkLabels (void) {
713 if (refFwd
!= NULL
) fatal("unresolved forward references to '@@' found");
714 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
715 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
718 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
);
724 static void freeLabels (void) {
727 LabelInfo
*l
= labels
;
730 freeLabelRefList(l
->fixes
);
731 freeLabelRefList(l
->refs
);
738 static void freeLocalLabels (int onlyVars
) {
739 LabelInfo
*p
= NULL
, *l
= labels
;
741 if (refFwd
!= NULL
) fatal("unresolved references to '@@' found");
744 LabelInfo
*n
= l
->next
;
746 if ((!onlyVars
|| l
->type
!= LB_CODE
) && l
->name
[0] == '.') {
747 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
750 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
);
752 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
753 freeLabelRefList(l
->fixes
);
754 freeLabelRefList(l
->refs
);
757 if (p
!= NULL
) p
->next
= n
; else labels
= n
;
766 static LabelInfo
*findLabel (const char *name
) {
767 if (name
&& strcmp(name
, "@@b") == 0) return &labelTempBack
;
768 if (name
&& strcmp(name
, "@@f") == 0) return &labelTempFwd
;
769 if (name
&& strcmp(name
, ".") == 0) {
770 //fprintf(stderr, "LOOKUP: '@@'\n");
773 if (name
&& name
[0] == '@') ++name
;
774 if (!name
|| !name
[0]) return NULL
;
775 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) if (strcmp(l
->name
, name
) == 0) return l
;
780 static LabelInfo
*addLabel (const char *name
) {
783 if (!name
|| !name
[0]) fatal("internal error: empty label name");
784 if (strcmp(name
, "@@b") == 0 || strcmp(name
, "@@f") == 0 || strcmp(name
, ".") == 0) fatal("can't define special label: '%s'", name
);
785 if (findLabel(name
)) fatal("duplicate label: '%s'", name
);
786 l
= calloc(1, sizeof(LabelInfo
));
787 if (l
== NULL
) fatal("out of memory");
788 l
->name
= strdup(name
);
789 if (l
->name
== NULL
) fatal("out of memory");
801 static void newTempLabel (void) {
804 //fprintf(stderr, "new '@@'\n");
808 ////////////////////////////////////////////////////////////////////////////////
810 LabelInfo
*l
; // !=NULL: label
812 int value
; // if l==NULL
813 int vartype
; // 0: global; 1: tlocal; 2: stack
817 static void setIntOperand (OperandInfo
*op
, int value
) {
825 static void setLabelOperand (OperandInfo
*op
, LabelInfo
*l
) {
833 static void getOperand (OperandInfo
*op
, int wantCode
) {
834 if (token
== TK_ID
) {
835 LabelInfo
*l
= findLabel(tstr
);
843 if (wantCode
&& l
->type
!= LB_CODE
) fatal("code offset expected");
845 setLabelOperand(op
, l
);
850 if (token
== TK_NUM
) {
851 if (wantCode
) fatal("code offset expected");
852 setIntOperand(op
, tint
);
858 //if (wantCode) fatal("code offset expected");
860 if (token
== TK_ID
) {
861 LabelInfo
*l
= findLabel(tstr
);
863 if (l
== NULL
|| l
->type
== LB_CODE
) fatal("unknown variable: '%s'", tstr
);
864 setLabelOperand(op
, l
);
865 } else if (token
== '@' || token
== '.') {
868 if (nextToken() != TK_NUM
) fatal("index expected");
869 setIntOperand(op
, tint
);
872 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
873 op
->vartype
= loc
=='@' ? 0 : 1;
878 } else if (token
== TK_NUM
) {
880 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
881 setIntOperand(op
, tint
);
884 fatal("invalid operand");
887 if (nextToken() != ']') fatal("']' expected");
892 fprintf(stderr
, "*%d [%s] %d\n", token
, tstr
, tint
);
893 fatal("invalid operand");
897 static inline int hasOperand (void) {
898 return (token
!= TK_EOF
&& token
!= TK_LABELDEF
&& token
< TK_VM_OPERATOR
);
902 ////////////////////////////////////////////////////////////////////////////////
903 static void emitByte (int b
) {
904 if (b
< 0 || b
> 255) fatal("internal error");
905 if (pc
>= 32768) fatal("code too big");
910 static void emitOpCode (int opc
, int opcount
) {
911 if (opc
< 0 || opc
> 63 || opcount
< 0 || opcount
> 3) fatal("internal error");
912 emitByte(opc
|(opcount
<<6));
917 static void emitInteger (int val) {
918 emitByte(255); // special
920 emitByte((val>>8)&0xff);
925 static void emitOperand (OperandInfo
*op
) {
926 if (!op
|| op
->var
< 0) return;
933 switch (op
->l
->type
) {
935 emitByte(op
->l
->value
|0x80);
936 addLabelRef(op
->l
, pc
-1);
937 if (op
->l
->ext
>= 0 && op
->l
->name
[0] != '.') addGVarFixup(pc
-1);
940 emitByte(op
->l
->value
);
941 addLabelRef(op
->l
, pc
-1);
942 if (op
->l
->ext
>= 0 && op
->l
->name
[0] != '.') addTVarFixup(pc
-1);
945 emitByte(127); // special
946 emitByte(op
->l
->value
&0xff);
947 emitByte((op
->l
->value
>>8)&0xff);
948 addLabelRef(op
->l
, pc
-2);
950 default: fatal("internal error");
954 switch (op
->vartype
) {
955 case 0: emitByte(op
->value
|0x80); break; // global
956 case 1: emitByte(op
->value
); break; // tlocal
958 emitByte(127); // special
959 emitByte(op
->value
&0xff);
960 emitByte((op
->value
>>8)&0xff);
962 default: fatal("internal error");
970 emitByte(255); // special
974 emitByte(op
->l
->value
&0xff);
975 emitByte((op
->l
->value
>>8)&0xff);
976 addLabelRef(op
->l
, pc
-2);
979 if (op
->value
< -32767 || op
->value
> 32767) fatal("invalid value");
980 emitByte(op
->value
&0xff);
981 emitByte((op
->value
>>8)&0xff);
987 static void emitInstruction (int opc
, OperandInfo
*op0
, OperandInfo
*op1
, OperandInfo
*op2
) {
990 if (op0
&& op0
->var
>= 0) ++ocnt
;
991 if (ocnt
== 1 && op1
&& op1
->var
>= 0) ++ocnt
;
992 if (ocnt
== 2 && op2
&& op2
->var
>= 0) ++ocnt
;
993 emitOpCode(opc
, ocnt
);
994 if (ocnt
> 0) emitOperand(op0
);
995 if (ocnt
> 1) emitOperand(op1
);
996 if (ocnt
> 2) emitOperand(op2
);
1000 ////////////////////////////////////////////////////////////////////////////////
1001 static void doNoOperands (int opcode
) {
1002 emitInstruction(opcode
, NULL
, NULL
, NULL
);
1006 static void doMath (int opcode
) {
1007 OperandInfo op0
, op1
, op2
;
1009 op0
.var
= op1
.var
= op2
.var
= -1;
1011 getOperand(&op0
, 0);
1014 getOperand(&op1
, 0);
1017 getOperand(&op2
, 0);
1018 if (op2
.var
!= 1) fatal("variable expected as third operand");
1020 if (op0
.var
!= 1) fatal("variable expected as first operand");
1024 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1028 static void doJXX (int opcode
) {
1029 OperandInfo op0
, op1
, op2
;
1031 op0
.var
= op1
.var
= op2
.var
= -1;
1033 getOperand(&op0
, 1);
1036 getOperand(&op1
, 0);
1039 getOperand(&op2
, 0);
1043 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1047 static void doBranch (int opcode
) {
1052 getOperand(&op0
, 1);
1054 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1058 static void doBSR (int opcode
) {
1059 OperandInfo op0
, op1
, op2
;
1061 op0
.var
= op1
.var
= op2
.var
= -1;
1063 getOperand(&op0
, 1);
1066 getOperand(&op1
, 0);
1069 getOperand(&op2
, 0);
1073 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1077 static void doMap (int opcode
, int lastMBV
) {
1078 OperandInfo op0
, op1
, op2
;
1080 op0
.var
= op1
.var
= op2
.var
= -1;
1082 getOperand(&op0
, 0);
1085 getOperand(&op1
, 0);
1088 getOperand(&op2
, 0);
1092 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1096 static void doRST (int opcode
) {
1097 OperandInfo op0
, op1
, op2
;
1099 op0
.var
= op1
.var
= op2
.var
= -1;
1101 getOperand(&op0
, 0);
1104 getOperand(&op1
, 0);
1107 getOperand(&op2
, 0);
1111 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1115 static void doSet (int opcode
) {
1116 OperandInfo op0
, op1
, op2
;
1118 op0
.var
= op1
.var
= op2
.var
= -1;
1119 getOperand(&op0
, 0);
1122 getOperand(&op1
, 0);
1125 getOperand(&op2
, 0);
1128 if (op2
.var
< 0 && op0
.var
!= 1) fatal("first operand should be var");
1129 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1133 static void doDrop (int opcode
) {
1137 if (hasOperand()) getOperand(&op0
, 0);
1138 if (op0
.var
> 0) fatal("number expected");
1139 if (op0
.value
< 0) fatal("positive number expected");
1140 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1144 static void doPush (int opcode
) {
1145 OperandInfo op0
, op1
, op2
;
1147 op0
.var
= op1
.var
= op2
.var
= -1;
1148 getOperand(&op0
, 0);
1151 getOperand(&op1
, 0);
1154 getOperand(&op2
, 0);
1157 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1161 static void doPop (int opcode
) {
1162 OperandInfo op0
, op1
, op2
;
1164 op0
.var
= op1
.var
= op2
.var
= -1;
1166 getOperand(&op0
, 0);
1169 getOperand(&op1
, 0);
1172 getOperand(&op2
, 0);
1176 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1180 static void doSwap (int opcode
) {
1181 OperandInfo op0
, op1
;
1183 op0
.var
= op1
.var
= -1;
1185 getOperand(&op0
, 0);
1188 getOperand(&op1
, 0);
1191 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1195 static void doDepth (int opcode
) {
1199 if (hasOperand()) getOperand(&op0
, 0);
1200 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1204 static void doPickRoll (int opcode
) {
1205 OperandInfo op0
, op1
;
1207 op0
.var
= op1
.var
= -1;
1209 getOperand(&op0
, 0);
1212 getOperand(&op1
, 0);
1213 if (op1
.var
!= 1) fatal("second argument must be variable");
1216 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1220 static void doNew (int opcode
) {
1221 OperandInfo op0
, op1
;
1223 op0
.var
= op1
.var
= -1;
1225 getOperand(&op0
, 0);
1228 getOperand(&op1
, 0);
1231 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1235 static void doTId (int opcode
) {
1239 if (hasOperand()) getOperand(&op0
, 0);
1240 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1244 static void doKillSusRes (int opcode
) {
1248 if (hasOperand()) getOperand(&op0
, 0);
1249 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1253 static void doSta (int opcode
) {
1254 OperandInfo op0
, op1
;
1256 op0
.var
= op1
.var
= -1;
1258 getOperand(&op0
, 0);
1261 getOperand(&op1
, 0);
1262 if (op1
.var
!= 1) fatal("variable expected");
1265 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1269 static void doGet (int opcode
) {
1270 OperandInfo op0
, op1
, op2
;
1272 op0
.var
= op1
.var
= op2
.var
= -1;
1273 getOperand(&op0
, 0);
1274 if (token
!= ',') fatal("at least two operands expected");
1276 getOperand(&op1
, 0);
1279 getOperand(&op2
, 0);
1280 if (op2
.var
!= 1) fatal("variable expected as third operand");
1282 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1286 static void doRXC (int opcode
) {
1287 OperandInfo op0
, op1
;
1289 op0
.var
= op1
.var
= -1;
1291 getOperand(&op0
, 0);
1294 getOperand(&op1
, 0);
1295 if (op1
.var
!= 1) fatal("variable expected as second operand");
1298 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1302 static void doWXC (int opcode
) {
1303 OperandInfo op0
, op1
;
1305 op0
.var
= op1
.var
= -1;
1307 getOperand(&op0
, 0);
1310 getOperand(&op1
, 0);
1313 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1317 static void doRet (int opcode
) {
1318 OperandInfo op0
, op1
, op2
;
1320 op0
.var
= op1
.var
= op2
.var
= -1;
1322 getOperand(&op0
, 0);
1323 if (token
!= ',') fatal("at least two operands expected");
1325 getOperand(&op1
, 0);
1328 getOperand(&op2
, 0);
1331 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1335 ////////////////////////////////////////////////////////////////////////////////
1336 static void parseInlcude (void) {
1337 static char fname
[8192], *t
;
1339 tokenWantFileName
= 1;
1340 if (nextToken() != TK_ID
) fatal("identifier expected");
1341 tokenWantFileName
= 0;
1342 strcpy(fname
, ictx
->fname
);
1344 for (t
= fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
1346 t
= strrchr(fname
, '/');
1347 if (t
!= NULL
) t
[1] = 0; else fname
[0] = 0;
1348 strcat(fname
, tstr
);
1349 if (incLevel
> 64) fatal("too many includes");
1355 static void parseVarList (int type
, int local
, int gotext
) {
1359 if (nextToken() != TK_ID
) fatal("identifier expected");
1360 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1361 if (tstr
[0] == '.' && local
<= 0) fatal("invalid variable name: '%s'", tstr
);
1362 if (tstr
[0] != '.' && local
> 0) fatal("invalid variable name: '%s'", tstr
);
1363 l
= findLabel(tstr
);
1366 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1367 if (l
->type
!= type
) fatal("can't change existing extern label type: '%s'", tstr
);
1369 fatal("duplicate variable or label: '%s'", tstr
);
1375 if (local
> 0 && vtloc
<= vtlast
) fatal("too many local vars");
1379 l
->value
= (local
>0)?(--vtloc
):(type
==LB_GVAR
?vglast
++:vtlast
++);
1381 if (local
< 0) l
->ext
= 1;
1383 if (gotext
) l
->ext
= -1;
1384 if (l
->value
> 126) fatal("too many vars");
1385 if (nextToken() != ',') break;
1390 static void parseUsedVarList (void) {
1394 if (nextToken() != TK_ID
) fatal("identifier expected");
1395 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1396 l
= findLabel(tstr
);
1397 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1399 if (nextToken() != ',') break;
1404 static void parseLocList (void) {
1408 if (nextToken() != TK_ID
) fatal("identifier expected");
1409 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1410 if (tstr
[0] != '.') fatal("local identifier expected instead of '%s'", tstr
);
1411 l
= findLabel(tstr
);
1412 if (l
!= NULL
) fatal("can't redefine label as local: '%s'", tstr
);
1417 if (nextToken() != '=') fatal("'=' expected");
1418 if (nextToken() != TK_NUM
) fatal("number expected");
1420 if (nextToken() != ',') break;
1425 static void parseConst (int ext
, int gotext
) {
1428 if (nextToken() != TK_ID
) fatal("identifier expected");
1429 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1430 if (tstr
[0] == '.') fatal("invalid constant name: '%s'", tstr
);
1431 l
= findLabel(tstr
);
1434 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1435 if (l
->type
!= LB_CONST
) fatal("can't change existing extern label type: '%s'", tstr
);
1437 fatal("constant must be unique: '%s'", tstr
);
1446 if (nextToken() == '=') nextToken();
1447 if (token
!= TK_NUM
) fatal("constant must be numeric");
1450 if (ext
) l
->ext
= 1;
1451 if (gotext
) l
->ext
= -1;
1456 static char procname
[8192];
1457 static int proclocals
= 0;
1458 static int procargs
= 0;
1459 static int lastWasReturn
= 0;
1462 static void parseProc (int ext
) {
1463 LabelInfo
*l
, *a
= NULL
;
1467 if (nextToken() != TK_ID
) fatal("identifier expected");
1468 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1469 if (tstr
[0] == '.') fatal("invalid proc name: '%s'", tstr
);
1470 if (procname
[0]) fatal("unclosed proc: '%s'", procname
);
1471 strcpy(procname
, tstr
);
1474 freeLocalLabels(0); // vars and code
1476 l
= findLabel(tstr
);
1478 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate proc label: '%s'", tstr
);
1479 fixupLabelRefs(l
, pc
);
1485 if (ext
) l
->ext
= 1;
1489 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "arg") == 0 || strcmp(tstr
, "args") == 0)) {
1491 if (nextToken() != TK_ID
) fatal("identifier expected");
1492 if (tstr
[0] != '.') fatal("argument name must starts with '.'");
1493 l
= findLabel(tstr
);
1494 if (l
!= NULL
) fatal("duplicate argument: '%s'", l
->name
);
1499 if (nextToken() != ',') break;
1503 // -1: return address, -2: last arg
1504 for (spt
= -2, l
= a
; l
->type
== LB_SVAR
; l
= l
->next
) {
1510 spt
= -1; // first local
1511 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "local") == 0 || strcmp(tstr
, "locals") == 0)) {
1513 if (nextToken() != TK_ID
) fatal("identifier expected");
1514 if (tstr
[0] != '.') fatal("local variable name must starts with '.'");
1515 l
= findLabel(tstr
);
1516 if (l
!= NULL
) fatal("duplicate local: '%s'", l
->name
);
1522 for (l
= a
; l
!= NULL
&& l
->type
== LB_SVAR
; l
= l
->next
) --(l
->value
);
1523 if (nextToken() != ',') break;
1527 if (proclocals
> 0) {
1531 setIntOperand(&op0
, -proclocals
);
1532 emitInstruction(VM_POP
, &op0
, NULL
, NULL
);
1537 static void parseEndP (void) {
1538 if (!procname
[0]) fatal("'endp' without 'proc'");
1539 if (nextToken() != TK_ID
) fatal("identifier expected");
1540 if (strcmp(procname
, tstr
) != 0) fatal("endp for '%s' in proc '%s'", tstr
, procname
);
1541 //if (!lastWasReturn) fatal("no 'return' in proc");
1548 static void doReturn (void) {
1549 OperandInfo op0
, op1
, op2
;
1551 if (!procname
[0]) fatal("'return' without 'proc'");
1553 op0
.var
= op1
.var
= op2
.var
= -1;
1554 if (hasOperand()) getOperand(&op2
, 0); // result
1555 setIntOperand(&op0
, proclocals
);
1556 setIntOperand(&op1
, procargs
);
1557 emitInstruction(VM_RET
, &op0
, &op1
, &op2
);
1561 static void parseLabel (int gotext
) {
1564 if (gotext
&& tstr
[0] == '.') fatal("can't declare local extern label: '%s'", tstr
);
1565 if (strcmp(tstr
, ".") == 0) {
1570 if (tstr
[0] != '.' && tstr
[0] != '@') {
1572 if (procname
[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname
);
1574 freeLocalLabels(0); // vars and code
1576 if (tstr
[0] == '@') {
1579 while (*d
++) d
[-1] = d
[0];
1582 l
= findLabel(tstr
);
1585 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1586 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
1588 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate label: '%s'", tstr
);
1589 fixupLabelRefs(l
, pc
);
1595 if (gotext
) l
->ext
= -1;
1600 static void parseDW (void) {
1602 LabelInfo
*l
= NULL
;
1604 if (token
== TK_ID
) {
1605 l
= findLabel(tstr
);
1611 //fatal("unknown label: '%s'", tstr);
1614 } else if (token
!= TK_NUM
) {
1615 fatal("number expected");
1617 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1618 emitByte(tint
&0xff);
1619 emitByte((tint
>>8)&0xff);
1621 addLabelRef(l
, pc
-2);
1622 addExternRef(l
, pc
-2, 2);
1623 if (l
->ext
>= 0 && l
->name
[0] != '.') {
1625 case LB_GVAR
: addGVarFixup(pc
-2); break;
1626 case LB_TVAR
: addTVarFixup(pc
-2); break;
1630 if (nextToken() != ',') break;
1636 static void parseAndPutString (int qch
) {
1638 int ch
= nextChar();
1640 //printf("[%c] [%c]\n", ch, qch);
1641 if (ch
== EOF
) fatal("unterminated string");
1643 if (ch
== qch
) break;
1648 if (ch
== EOF
) fatal("invalid escape");
1650 case 'a': emitByte('\a'); break;
1651 case 'b': emitByte('\b'); break;
1652 case 'e': emitByte('\x1b'); break;
1653 case 'f': emitByte('\f'); break;
1654 case 'n': emitByte('\n'); break;
1655 case 'r': emitByte('\r'); break;
1656 case 't': emitByte('\t'); break;
1657 case 'v': emitByte('\v'); break;
1658 case '"': case '\'': case '\\': case ' ': emitByte(ch
); break;
1660 n
= digit(nextChar(), 16);
1661 if (n
< 0) fatal("invalid hex escape");
1663 if (ch
== EOF
) fatal("invalid hex escape");
1664 if (digit(ch
, 16) >= 0) {
1665 n
= n
*16+digit(ch
, 16);
1671 default: fatal("invalid escape: '%c'", ch
);
1679 if (ch
== EOF
) return;
1694 static void parseDAscii (int zeroend
) {
1699 if (token
== TK_EOF
) break;
1701 case '"': tokenWantFileName
= 1; parseAndPutString(token
); tokenWantFileName
= 0; break;
1702 case '\'': tokenWantFileName
= 1; parseAndPutString(token
); tokenWantFileName
= 0; break;
1704 l
= findLabel(tstr
);
1706 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1707 if (l
->type
== LB_CODE
) fatal("bad label type: '%s'", l
->name
);
1709 addExternRef(l
, pc
, 1);
1710 if (l
->ext
>= 0 && l
->name
[0] != '.') {
1712 case LB_GVAR
: addGVarFixup(pc
); break;
1713 case LB_TVAR
: addTVarFixup(pc
); break;
1719 if (tint
< -128 || tint
> 255) fatal("bad value: %d", tint
);
1720 emitByte(tint
&0xff);
1723 fatal("number expected");
1725 if (nextToken() != ',') break;
1727 if (zeroend
) emitByte(0);
1731 static void parsePublics (void) {
1736 if (token
== TK_LABELDEF
) {
1740 if (token
!= TK_ID
) fatal("identifier expected");
1741 if (tstr
[0] == '.') fatal("invalid label name: '%s'", tstr
);
1742 l
= findLabel(tstr
);
1751 if (nextToken() != ',') break;
1756 static void parseMacroDef (void) {
1760 int tpos
= 0, tsize
= 0;
1762 void addChar (int c
) {
1763 if (tpos
+1 > tsize
) {
1764 int newsz
= tsize
+1024;
1765 char *nn
= realloc(text
, newsz
);
1767 if (nn
== NULL
) fatal("out of memory");
1774 if (nextToken() != TK_ID
) fatal("macro name expected");
1775 if (tstr
[0] == '.') fatal("macros can't be local: '%s'", tstr
);
1776 for (mc
= macros
; mc
!= NULL
; mc
= mc
->next
) if (strcmp(mc
->name
, tstr
) == 0) fatal("macros can't be redefined: '%s'", tstr
);
1777 if ((mc
= calloc(1, sizeof(MacroDef
))) == NULL
) fatal("out of memory");
1779 if ((mc
->name
= strdup(tstr
)) == NULL
) fatal("out of memory");
1781 // now parse macro args
1784 skipSpaces(0); // no newlines allowed here
1790 if (nextToken() != TK_ID
) fatal("invalid macro argument definition");
1791 if (tstr
[0] == '.') fatal("macro argument name must not start with dot: '%s'", tstr
);
1792 addMacroArgDef(mc
, tstr
);
1795 if (c
!= ',') { ungetChar(c
); break; }
1798 // now parse macro text
1804 case EOF
: fatal("incomplete macro definition: '%s'", mc
->name
);
1805 case '\n': // check for macro end
1807 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1808 if (c
!= 'e') { ungetChar(c
); break; }
1809 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1810 if (c
!= 'n') { ungetChar(c
); addChar('e'); break; }
1811 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1812 if (c
!= 'd') { ungetChar(c
); addChar('e'); addChar('n'); break; }
1813 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1814 if (c
!= 'm') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); break; }
1815 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1816 if (c
!= 'a') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); break; }
1817 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1818 if (c
!= 'c') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); break; }
1819 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1820 if (c
!= 'r') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); break; }
1821 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1822 if (c
!= 'o') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); break; }
1824 if (c
== EOF
) goto macro_complete
;
1825 if (!isspace(c
) && c
!= ';' && c
!= '/') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); addChar('o'); break; }
1827 skipSpaces(0); // no EOLs
1829 if (c
!= '\n') fatal("invalid endmacro: '%s' (%d)", mc
->name
, c
);
1830 goto macro_complete
;
1835 tokenWantFileName
= 1;
1838 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1840 if (c
== '"') break;
1843 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1847 tokenWantFileName
= 0;
1849 case '\'': // string
1851 tokenWantFileName
= 1;
1854 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1858 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1859 if (c
!= '\'') { ungetChar(c
); break; }
1863 tokenWantFileName
= 0;
1865 case ';': // comment
1870 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1871 if (c
== '\n') break;
1876 if (c1
== '/') goto one_line_comment
; // comment
1878 // multiline comment
1881 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1884 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1885 if (c
== '/') break;
1888 } else if (c1
== EOF
) {
1889 fatal("incomplete macro definition: '%s'", mc
->name
);
1907 static void parseMacroInvocation (MacroDef
*mc
) {
1908 MStrListItem
*args
= NULL
;
1910 if (mc
->argnames
!= NULL
) {
1911 nextToken(); // skip macro name
1912 // collect macro arguments
1913 for (MStrListItem
*a
= mc
->argnames
; a
!= NULL
; a
= a
->next
) {
1917 case TK_EOF
: fatal("macro argument expected: '%s'", mc
->name
);
1918 case TK_LABELDEF
: fatal("macro argument can't be label: '%s'", mc
->name
);
1919 case TK_NUM
: sprintf(tstr
, "%d", tint
); break;
1920 case TK_ID
: break; // ok
1923 tstr
[pos
++] = token
;
1924 tokenWantFileName
= 1;
1927 if (c
== EOF
) fatal("incomplete macro argument: '%s'", mc
->name
);
1928 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1930 if (c
== '"') break;
1933 if (c
== EOF
) fatal("incomplete macro argument: '%s'", mc
->name
);
1934 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1938 tokenWantFileName
= 0;
1941 case '\'': // string
1943 tstr
[pos
++] = token
;
1944 tokenWantFileName
= 1;
1947 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1948 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1952 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1953 if (c
!= '\'') { ungetChar(c
); break; }
1954 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1958 tokenWantFileName
= 0;
1962 if (token
>= TK_VM_OPERATOR
) break;
1964 tstr
[pos
++] = token
;
1967 if (c
== EOF
) break;//fatal("incomplete macro definition: '%s'", mc->name);
1968 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1969 //TODO: better comment parsing
1970 if (c
== ',' || c
== ';' || c
== '/' || c
== '\n') { ungetChar(c
); break; }
1973 while (isspace(tstr
[pos
-1])) --pos
;
1976 //fatal("invalid macro argument: '%s'", mc->name);
1978 //fprintf(stderr, "arg [%s]: [%s]\n", mc->argnames->macname, tstr);
1979 args
= addMacroArg(args
, mc
->argnames
->macname
, tstr
);
1980 if (a
->next
!= NULL
) {
1981 if (nextToken() != ',') fatal("macro argument expected: '%s'", mc
->name
);
1985 // arguments collected
1986 openMacro(mc
, args
);
1991 static void parseLabelDef () {
1994 if (nextToken() != TK_ID
) fatal("label name expected");
1996 l
= findLabel(tstr
);
1997 if (strcmp(tstr
, ".") == 0) fatal("can't declare temp label");
2001 if (l
->ext
< 0) fatal("can't declare extern label: '%s'", tstr
);
2002 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("can't redeclare label: '%s'", tstr
);
2003 //fixupLabelRefs(l, pc);
2010 if (nextToken() == '=') {
2012 //fprintf(stderr, "token=%d\n", token);
2016 //TODO: allow simple repeated math (with labels too)
2018 skipSpaces(0); // not newlines
2020 if (ch
== '-' || ch
== '+') {
2022 if (token
!= TK_NUM
) fatal("number expected");
2023 if (ch
== '-') tint
= -tint
;
2025 //fprintf(stderr, "pc=%d; tint=%d; value=%d\n", pc, tint, l->value);
2027 if (ch
!= EOF
&& ch
!= '\n') fatal("invalid label definition: '%s'", l
->name
);
2029 } else if (token
== TK_NUM
) {
2032 fatal("invalid label definition: '%s'", l
->name
);
2038 if (l
->value
< 0) l
->value
&= 0xffff;
2039 fixupLabelRefs(l
, l
->value
);
2043 static void process (void) {
2045 LabelInfo
*l
= addLabel("retval");
2048 l
->value
= VM_VARS_SIZE
-1;
2050 memset(&labelTempBack
, 0, sizeof(LabelInfo
));
2051 memset(&labelTempFwd
, 0, sizeof(labelTempFwd
));
2053 labelTempBack
.type
= labelTempFwd
.type
= LB_CODE
;
2054 labelTempBack
.value
= labelTempFwd
.value
= -1;
2059 while (token
!= TK_EOF
) {
2062 if (token
== TK_LABELDEF
) {
2063 if (strcmp(tstr
, "extern") == 0) {
2064 if (gotext
) fatal("double 'extern'");
2069 // new label or operator
2070 if (strcmp(tstr
, "include") == 0) {
2073 } else if (strcmp(tstr
, "defloc") == 0) {
2074 if (gotext
) fatal("extern labels must not be locals");
2076 } else if (strcmp(tstr
, "defgvar") == 0) {
2077 parseVarList(LB_GVAR
, 0, gotext
);
2078 } else if (strcmp(tstr
, "defevar") == 0) { // extern global
2079 parseVarList(LB_GVAR
, -1, gotext
);
2080 } else if (strcmp(tstr
, "deftvar") == 0) {
2081 freeLocalLabels(1); // only vars
2082 vtloc
= VM_VARS_SIZE
;
2083 parseVarList(LB_TVAR
, 0, gotext
);
2084 } else if (strcmp(tstr
, "defetvar") == 0) {
2085 freeLocalLabels(1); // only vars
2086 vtloc
= VM_VARS_SIZE
;
2087 parseVarList(LB_TVAR
, -1, gotext
);
2088 } else if (strcmp(tstr
, "deflvar") == 0) {
2089 if (gotext
) fatal("extern labels must not be locals");
2090 parseVarList(LB_TVAR
, 1, gotext
);
2091 } else if (strcmp(tstr
, "public") == 0) {
2092 if (gotext
) fatal("invalid extern label declaration");
2094 } else if (strcmp(tstr
, "proc") == 0 || strcmp(tstr
, "eproc") == 0) {
2095 if (gotext
) fatal("invalid extern label declaration");
2096 parseProc(tstr
[0] == 'e');
2097 } else if (strcmp(tstr
, "endp") == 0) {
2098 if (gotext
) fatal("invalid extern label declaration");
2100 } else if (strcmp(tstr
, "const") == 0 || strcmp(tstr
, "econst") == 0) {
2101 parseConst(tstr
[0] == 'e', gotext
);
2102 } else if (strcmp(tstr
, "db") == 0) {
2103 if (gotext
) fatal("invalid extern label declaration");
2105 } else if (strcmp(tstr
, "dw") == 0) {
2106 if (gotext
) fatal("invalid extern label declaration");
2108 } else if (strcmp(tstr
, "da") == 0) {
2109 if (gotext
) fatal("invalid extern label declaration");
2111 } else if (strcmp(tstr
, "dz") == 0) {
2112 if (gotext
) fatal("invalid extern label declaration");
2114 } else if (strcmp(tstr
, "macro") == 0) {
2115 if (gotext
) fatal("macros can't be external");
2117 } else if (strcmp(tstr
, "used") == 0) {
2118 if (gotext
) fatal("'used' labels can't be external");
2120 } else if (strcmp(tstr
, "gvarbase") == 0) {
2121 if (gotext
) fatal("'gvarbase' can't be external");
2122 if (nextToken() != TK_NUM
) fatal("gvarbase: number expected");
2125 } else if (strcmp(tstr
, "tvarbase") == 0) {
2126 if (gotext
) fatal("'tvarbase' can't be external");
2127 if (nextToken() != TK_NUM
) fatal("tvarbase: number expected");
2130 } else if (strcmp(tstr
, "label") == 0) {
2131 if (gotext
) fatal("'label' can't be external");
2142 if (token
!= TK_ID
) fatal("label declaration expected after 'extern'");
2143 if (tstr
[0] == '.') fatal("extern label can't be local: '%s'", tstr
);
2144 l
= findLabel(tstr
);
2146 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
2147 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
2158 // check for macro invocation
2159 if (token
== TK_ID
) {
2160 for (MacroDef
*mc
= macros
; mc
!= NULL
; mc
= mc
->next
) {
2161 if (strcmp(mc
->name
, tstr
) == 0) {
2162 parseMacroInvocation(mc
);
2168 if (token
< TK_VM_OPERATOR
) {
2169 //fprintf(stderr, "%d: [%s]\n", token, tstr);
2170 fatal("mnemonics expected");
2173 if (opc
< 600) opc
-= TK_VM_OPERATOR
;
2261 if (procname
[0]) doReturn(); else doRet(opc
);
2267 doNoOperands(VM_PSH
);
2279 if (procname
[0]) fatal("'proc' without 'endp': '%s'", procname
);
2283 ////////////////////////////////////////////////////////////////////////////////
2284 static int cfWriteByte (FILE *fl
, int value
) {
2287 b
= (value
&0xff)^SECRET
;
2288 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
2293 static int cfWriteWord (FILE *fl
, int value
) {
2294 if (cfWriteByte(fl
, value
) != 0) return -1;
2295 if (cfWriteByte(fl
, value
>>8) != 0) return -1;
2300 static int cfWriteCode (FILE *fl
) {
2301 for (int f
= 0; f
< pc
; ++f
) if (cfWriteByte(fl
, vmcode
[f
]) != 0) return -1;
2306 static int cfWriteRels (FILE *fl
) {
2307 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) {
2308 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
2314 static int cfWritePublicLabels (FILE *fl
) {
2315 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2316 if (l
->ext
>= 0 && l
->name
[0]) {
2317 int len
= strlen(l
->name
);
2319 if (cfWriteByte(fl
, l
->type
|(l
->ext
>0?0x80:0)) != 0) return -1;
2324 if (cfWriteByte(fl
, l
->value
) != 0) return -1;
2329 if (cfWriteWord(fl
, l
->value
) != 0) return -1;
2335 if (len
> 255) len
= 255;
2336 if (cfWriteByte(fl
, len
) != 0) return -1;
2337 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
2344 static int cfWriteExtLabels (FILE *fl
) {
2345 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2346 if (l
->ext
< 0 && l
->name
[0] && l
->refs
!= NULL
) {
2347 int len
= strlen(l
->name
), rcnt
= 0;
2349 if (cfWriteByte(fl
, l
->type
) != 0) return -1;
2350 if (len
> 255) len
= 255;
2351 if (cfWriteByte(fl
, len
) != 0) return -1;
2352 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
2354 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
2355 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
2356 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) {
2357 if (cfWriteByte(fl
, r
->size
) != 0) return -1;
2358 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
2366 static int cfWriteVarFixups (FILE *fl
, const VarFixup
*list
) {
2369 for (const VarFixup
*f
= list
; f
!= NULL
; f
= f
->next
) ++cnt
;
2370 if (cfWriteWord(fl
, cnt
) != 0) return -1;
2371 for (const VarFixup
*f
= list
; f
!= NULL
; f
= f
->next
) {
2372 if (cfWriteWord(fl
, f
->pc
) != 0) return -1;
2378 static int writeCodeFile (FILE *fl
) {
2379 static const char *sign
= "AVM2";
2380 int lcnt
= 0, elcnt
= 0;
2383 relrefs
= lrefRemoveDups(relrefs
);
2384 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
2385 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2387 if (l
->ext
>= 0) ++lcnt
;
2389 l
->refs
= lrefRemoveDups(l
->refs
);
2390 if (l
->refs
!= NULL
) ++elcnt
;
2394 fprintf(stderr
, "%d bytes of code, %d public labels, %d fixups, %d externs; maxgvar: %d, maxtvar: %d\n", pc
, lcnt
, rcnt
, elcnt
, vglast
, vtlast
);
2396 if (fwrite(sign
, 4, 1, fl
) != 1) return -1;
2398 if (cfWriteWord(fl
, pc
) != 0) return -1;
2400 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
2401 // number of extern labels
2402 if (cfWriteWord(fl
, elcnt
) != 0) return -1;
2404 if (cfWriteWord(fl
, lcnt
) != 0) return -1;
2406 if (cfWriteWord(fl
, vglast
) != 0) return -1;
2407 // last used thread local
2408 if (cfWriteWord(fl
, vtlast
) != 0) return -1;
2410 if (cfWriteCode(fl
) != 0) return -1;
2411 if (cfWriteRels(fl
) != 0) return -1;
2412 if (cfWriteExtLabels(fl
) != 0) return -1;
2413 if (cfWritePublicLabels(fl
) != 0) return -1;
2414 if (cfWriteVarFixups(fl
, gvfixes
) != 0) return -1;
2415 if (cfWriteVarFixups(fl
, tvfixes
) != 0) return -1;
2421 ////////////////////////////////////////////////////////////////////////////////
2423 # include "cmdline.c"
2427 ////////////////////////////////////////////////////////////////////////////////
2428 int main (int argc
, char *argv
[]) {
2435 for (int f
= 1; f
< argc
; ++f
) {
2436 if (strcmp(argv
[f
], "-Wno") == 0) {
2438 for (int c
= f
+1; c
< argc
; ++c
) argv
[c
-1] = argv
[c
];
2439 argv
[--argc
] = NULL
;
2446 fprintf(stderr
, "usage: awasm infile outfile\n");
2450 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
2452 while (ictx
!= NULL
) closeFile();
2453 freeLocalLabels(0); // vars and code
2455 //if (argc > 3) dumpGlobalVars(argv[3]);
2457 FILE *fo
= fopen(argv
[2], "wb");
2460 if (fo
== NULL
) { fprintf(stderr
, "FATAL: can't create output file: '%s'\n", argv
[2]); return 1; }
2461 res
= writeCodeFile(fo
);
2462 if (fclose(fo
) != 0) res
= -1;
2464 fprintf(stderr
, "FATAL: error writing output file: '%s'\n", argv
[2]);
2470 freeLabelRefList(relrefs
);