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;
667 if (l
== &labelTempFwd
) {
668 //fprintf(stderr, "fwdref to '@@'\n");
669 refFwd
= addLabelRefToList(refFwd
, pc
);
674 addExternRef(l
, pc
, -1);
675 if (l
->type
== LB_CODE
&& l
->ext
>= 0) {
676 // record fixup info for undefined code labels
677 if (l
->value
< 0) l
->fixes
= addLabelRefToList(l
->fixes
, pc
);
678 // record reference info for code labels
679 relrefs
= addLabelRefToList(relrefs
, pc
);
685 static void fixupFwdTmpRefs (void) {
686 for (LabelRefInfo
*fix
= refFwd
; fix
!= NULL
; fix
= fix
->next
) {
687 vmcode
[fix
->pc
+0] = pc
&0xff;
688 vmcode
[fix
->pc
+1] = (pc
>>8)&0xff;
690 freeLabelRefList(refFwd
);
695 static void fixupLabelRefs (LabelInfo
*l
, int pc
) {
696 if (l
== &labelTempBack
|| l
== &labelTempFwd
) return;
700 for (LabelRefInfo
*fix
= l
->fixes
; fix
!= NULL
; fix
= fix
->next
) {
701 vmcode
[fix
->pc
+0] = (l
->value
)&0xff;
702 vmcode
[fix
->pc
+1] = ((l
->value
)>>8)&0xff;
704 freeLabelRefList(l
->fixes
);
710 static void checkLabels (void) {
711 if (refFwd
!= NULL
) fatal("unresolved forward references to '@@' found");
712 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
713 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
716 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
);
722 static void freeLabels (void) {
725 LabelInfo
*l
= labels
;
728 freeLabelRefList(l
->fixes
);
729 freeLabelRefList(l
->refs
);
736 static void freeLocalLabels (int onlyVars
) {
737 LabelInfo
*p
= NULL
, *l
= labels
;
739 if (refFwd
!= NULL
) fatal("unresolved references to '@@' found");
742 LabelInfo
*n
= l
->next
;
744 if ((!onlyVars
|| l
->type
!= LB_CODE
) && l
->name
[0] == '.') {
745 if (l
->type
== LB_CODE
&& l
->ext
>= 0 && l
->value
< 0) fatal("undefined %slabel: '%s'", l
->used
?"":"not referenced ", l
->name
);
748 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
);
750 //if (l->type == LB_CODE && l->value < 0) fatal("undefined label: '%s'", l->name);
751 freeLabelRefList(l
->fixes
);
752 freeLabelRefList(l
->refs
);
755 if (p
!= NULL
) p
->next
= n
; else labels
= n
;
764 static LabelInfo
*findLabel (const char *name
) {
765 if (name
&& strcmp(name
, "@@b") == 0) return &labelTempBack
;
766 if (name
&& strcmp(name
, "@@f") == 0) return &labelTempFwd
;
767 if (name
&& strcmp(name
, ".") == 0) {
768 //fprintf(stderr, "LOOKUP: '@@'\n");
771 if (name
&& name
[0] == '@') ++name
;
772 if (!name
|| !name
[0]) return NULL
;
773 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) if (strcmp(l
->name
, name
) == 0) return l
;
778 static LabelInfo
*addLabel (const char *name
) {
781 if (!name
|| !name
[0]) fatal("internal error: empty label name");
782 if (strcmp(name
, "@@b") == 0 || strcmp(name
, "@@f") == 0 || strcmp(name
, ".") == 0) fatal("can't define special label: '%s'", name
);
783 if (findLabel(name
)) fatal("duplicate label: '%s'", name
);
784 l
= calloc(1, sizeof(LabelInfo
));
785 if (l
== NULL
) fatal("out of memory");
786 l
->name
= strdup(name
);
787 if (l
->name
== NULL
) fatal("out of memory");
799 static void newTempLabel (void) {
802 //fprintf(stderr, "new '@@'\n");
806 ////////////////////////////////////////////////////////////////////////////////
808 LabelInfo
*l
; // !=NULL: label
810 int value
; // if l==NULL
811 int vartype
; // 0: global; 1: tlocal; 2: stack
815 static void setIntOperand (OperandInfo
*op
, int value
) {
823 static void setLabelOperand (OperandInfo
*op
, LabelInfo
*l
) {
831 static void getOperand (OperandInfo
*op
, int wantCode
) {
832 if (token
== TK_ID
) {
833 LabelInfo
*l
= findLabel(tstr
);
841 if (wantCode
&& l
->type
!= LB_CODE
) fatal("code offset expected");
843 setLabelOperand(op
, l
);
848 if (token
== TK_NUM
) {
849 if (wantCode
) fatal("code offset expected");
850 setIntOperand(op
, tint
);
856 //if (wantCode) fatal("code offset expected");
858 if (token
== TK_ID
) {
859 LabelInfo
*l
= findLabel(tstr
);
861 if (l
== NULL
|| l
->type
== LB_CODE
) fatal("unknown variable: '%s'", tstr
);
862 setLabelOperand(op
, l
);
863 } else if (token
== '@' || token
== '.') {
866 if (nextToken() != TK_NUM
) fatal("index expected");
867 setIntOperand(op
, tint
);
870 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
871 op
->vartype
= loc
=='@' ? 0 : 1;
876 } else if (token
== TK_NUM
) {
878 if (tint
< 0 || tint
> 126) fatal("invalid variable index");
879 setIntOperand(op
, tint
);
882 fatal("invalid operand");
885 if (nextToken() != ']') fatal("']' expected");
890 fprintf(stderr
, "*%d [%s] %d\n", token
, tstr
, tint
);
891 fatal("invalid operand");
895 static inline int hasOperand (void) {
896 return (token
!= TK_EOF
&& token
!= TK_LABELDEF
&& token
< TK_VM_OPERATOR
);
900 ////////////////////////////////////////////////////////////////////////////////
901 static void emitByte (int b
) {
902 if (b
< 0 || b
> 255) fatal("internal error");
903 if (pc
>= 32768) fatal("code too big");
908 static void emitOpCode (int opc
, int opcount
) {
909 if (opc
< 0 || opc
> 63 || opcount
< 0 || opcount
> 3) fatal("internal error");
910 emitByte(opc
|(opcount
<<6));
915 static void emitInteger (int val) {
916 emitByte(255); // special
918 emitByte((val>>8)&0xff);
923 static void emitOperand (OperandInfo
*op
) {
924 if (!op
|| op
->var
< 0) return;
931 switch (op
->l
->type
) {
933 emitByte(op
->l
->value
|0x80);
934 addLabelRef(op
->l
, pc
-1);
935 if (op
->l
->ext
>= 0 && op
->l
->name
[0] != '.') addGVarFixup(pc
-1);
938 emitByte(op
->l
->value
);
939 addLabelRef(op
->l
, pc
-1);
940 if (op
->l
->ext
>= 0 && op
->l
->name
[0] != '.') addTVarFixup(pc
-1);
943 emitByte(127); // special
944 emitByte(op
->l
->value
&0xff);
945 emitByte((op
->l
->value
>>8)&0xff);
946 addLabelRef(op
->l
, pc
-2);
948 default: fatal("internal error");
952 switch (op
->vartype
) {
953 case 0: emitByte(op
->value
|0x80); break; // global
954 case 1: emitByte(op
->value
); break; // tlocal
956 emitByte(127); // special
957 emitByte(op
->value
&0xff);
958 emitByte((op
->value
>>8)&0xff);
960 default: fatal("internal error");
968 emitByte(255); // special
972 emitByte(op
->l
->value
&0xff);
973 emitByte((op
->l
->value
>>8)&0xff);
974 addLabelRef(op
->l
, pc
-2);
977 if (op
->value
< -32767 || op
->value
> 32767) fatal("invalid value");
978 emitByte(op
->value
&0xff);
979 emitByte((op
->value
>>8)&0xff);
985 static void emitInstruction (int opc
, OperandInfo
*op0
, OperandInfo
*op1
, OperandInfo
*op2
) {
988 if (op0
&& op0
->var
>= 0) ++ocnt
;
989 if (ocnt
== 1 && op1
&& op1
->var
>= 0) ++ocnt
;
990 if (ocnt
== 2 && op2
&& op2
->var
>= 0) ++ocnt
;
991 emitOpCode(opc
, ocnt
);
992 if (ocnt
> 0) emitOperand(op0
);
993 if (ocnt
> 1) emitOperand(op1
);
994 if (ocnt
> 2) emitOperand(op2
);
998 ////////////////////////////////////////////////////////////////////////////////
999 static void doNoOperands (int opcode
) {
1000 emitInstruction(opcode
, NULL
, NULL
, NULL
);
1004 static void doMath (int opcode
) {
1005 OperandInfo op0
, op1
, op2
;
1007 op0
.var
= op1
.var
= op2
.var
= -1;
1009 getOperand(&op0
, 0);
1012 getOperand(&op1
, 0);
1015 getOperand(&op2
, 0);
1016 if (op2
.var
!= 1) fatal("variable expected as third operand");
1018 if (op0
.var
!= 1) fatal("variable expected as first operand");
1022 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1026 static void doJXX (int opcode
) {
1027 OperandInfo op0
, op1
, op2
;
1029 op0
.var
= op1
.var
= op2
.var
= -1;
1031 getOperand(&op0
, 1);
1034 getOperand(&op1
, 0);
1037 getOperand(&op2
, 0);
1041 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1045 static void doBranch (int opcode
) {
1050 getOperand(&op0
, 1);
1052 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1056 static void doBSR (int opcode
) {
1057 OperandInfo op0
, op1
, op2
;
1059 op0
.var
= op1
.var
= op2
.var
= -1;
1061 getOperand(&op0
, 1);
1064 getOperand(&op1
, 0);
1067 getOperand(&op2
, 0);
1071 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1075 static void doMap (int opcode
, int lastMBV
) {
1076 OperandInfo op0
, op1
, op2
;
1078 op0
.var
= op1
.var
= op2
.var
= -1;
1080 getOperand(&op0
, 0);
1083 getOperand(&op1
, 0);
1086 getOperand(&op2
, 0);
1090 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1094 static void doRST (int opcode
) {
1095 OperandInfo op0
, op1
, op2
;
1097 op0
.var
= op1
.var
= op2
.var
= -1;
1099 getOperand(&op0
, 0);
1102 getOperand(&op1
, 0);
1105 getOperand(&op2
, 0);
1109 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1113 static void doSet (int opcode
) {
1114 OperandInfo op0
, op1
, op2
;
1116 op0
.var
= op1
.var
= op2
.var
= -1;
1117 getOperand(&op0
, 0);
1120 getOperand(&op1
, 0);
1123 getOperand(&op2
, 0);
1126 if (op2
.var
< 0 && op0
.var
!= 1) fatal("first operand should be var");
1127 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1131 static void doDrop (int opcode
) {
1135 if (hasOperand()) getOperand(&op0
, 0);
1136 if (op0
.var
> 0) fatal("number expected");
1137 if (op0
.value
< 0) fatal("positive number expected");
1138 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1142 static void doPush (int opcode
) {
1143 OperandInfo op0
, op1
, op2
;
1145 op0
.var
= op1
.var
= op2
.var
= -1;
1146 getOperand(&op0
, 0);
1149 getOperand(&op1
, 0);
1152 getOperand(&op2
, 0);
1155 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1159 static void doPop (int opcode
) {
1160 OperandInfo op0
, op1
, op2
;
1162 op0
.var
= op1
.var
= op2
.var
= -1;
1164 getOperand(&op0
, 0);
1167 getOperand(&op1
, 0);
1170 getOperand(&op2
, 0);
1174 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1178 static void doSwap (int opcode
) {
1179 OperandInfo op0
, op1
;
1181 op0
.var
= op1
.var
= -1;
1183 getOperand(&op0
, 0);
1186 getOperand(&op1
, 0);
1189 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1193 static void doDepth (int opcode
) {
1197 if (hasOperand()) getOperand(&op0
, 0);
1198 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1202 static void doPickRoll (int opcode
) {
1203 OperandInfo op0
, op1
;
1205 op0
.var
= op1
.var
= -1;
1207 getOperand(&op0
, 0);
1210 getOperand(&op1
, 0);
1211 if (op1
.var
!= 1) fatal("second argument must be variable");
1214 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1218 static void doNew (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 doTId (int opcode
) {
1237 if (hasOperand()) getOperand(&op0
, 0);
1238 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1242 static void doKillSusRes (int opcode
) {
1246 if (hasOperand()) getOperand(&op0
, 0);
1247 emitInstruction(opcode
, &op0
, NULL
, NULL
);
1251 static void doSta (int opcode
) {
1252 OperandInfo op0
, op1
;
1254 op0
.var
= op1
.var
= -1;
1256 getOperand(&op0
, 0);
1259 getOperand(&op1
, 0);
1260 if (op1
.var
!= 1) fatal("variable expected");
1263 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1267 static void doGet (int opcode
) {
1268 OperandInfo op0
, op1
, op2
;
1270 op0
.var
= op1
.var
= op2
.var
= -1;
1271 getOperand(&op0
, 0);
1272 if (token
!= ',') fatal("at least two operands expected");
1274 getOperand(&op1
, 0);
1277 getOperand(&op2
, 0);
1278 if (op2
.var
!= 1) fatal("variable expected as third operand");
1280 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1284 static void doRXC (int opcode
) {
1285 OperandInfo op0
, op1
;
1287 op0
.var
= op1
.var
= -1;
1289 getOperand(&op0
, 0);
1292 getOperand(&op1
, 0);
1293 if (op1
.var
!= 1) fatal("variable expected as second operand");
1296 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1300 static void doWXC (int opcode
) {
1301 OperandInfo op0
, op1
;
1303 op0
.var
= op1
.var
= -1;
1305 getOperand(&op0
, 0);
1308 getOperand(&op1
, 0);
1311 emitInstruction(opcode
, &op0
, &op1
, NULL
);
1315 static void doRet (int opcode
) {
1316 OperandInfo op0
, op1
, op2
;
1318 op0
.var
= op1
.var
= op2
.var
= -1;
1320 getOperand(&op0
, 0);
1321 if (token
!= ',') fatal("at least two operands expected");
1323 getOperand(&op1
, 0);
1326 getOperand(&op2
, 0);
1329 emitInstruction(opcode
, &op0
, &op1
, &op2
);
1333 ////////////////////////////////////////////////////////////////////////////////
1334 static void parseInlcude (void) {
1335 static char fname
[8192], *t
;
1337 tokenWantFileName
= 1;
1338 if (nextToken() != TK_ID
) fatal("identifier expected");
1339 tokenWantFileName
= 0;
1340 strcpy(fname
, ictx
->fname
);
1342 for (t
= fname
; *t
; ++t
) if (*t
== '\\') *t
= '/';
1344 t
= strrchr(fname
, '/');
1345 if (t
!= NULL
) t
[1] = 0; else fname
[0] = 0;
1346 strcat(fname
, tstr
);
1347 if (incLevel
> 64) fatal("too many includes");
1353 static void parseVarList (int type
, int local
, int gotext
) {
1357 if (nextToken() != TK_ID
) fatal("identifier expected");
1358 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1359 if (tstr
[0] == '.' && local
<= 0) fatal("invalid variable name: '%s'", tstr
);
1360 if (tstr
[0] != '.' && local
> 0) fatal("invalid variable name: '%s'", tstr
);
1361 l
= findLabel(tstr
);
1364 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1365 if (l
->type
!= type
) fatal("can't change existing extern label type: '%s'", tstr
);
1367 fatal("duplicate variable or label: '%s'", tstr
);
1373 if (local
> 0 && vtloc
<= vtlast
) fatal("too many local vars");
1377 l
->value
= (local
>0)?(--vtloc
):(type
==LB_GVAR
?vglast
++:vtlast
++);
1379 if (local
< 0) l
->ext
= 1;
1381 if (gotext
) l
->ext
= -1;
1382 if (l
->value
> 126) fatal("too many vars");
1383 if (nextToken() != ',') break;
1388 static void parseUsedVarList (void) {
1392 if (nextToken() != TK_ID
) fatal("identifier expected");
1393 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1394 l
= findLabel(tstr
);
1395 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1397 if (nextToken() != ',') break;
1402 static void parseLocList (void) {
1406 if (nextToken() != TK_ID
) fatal("identifier expected");
1407 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1408 if (tstr
[0] != '.') fatal("local identifier expected instead of '%s'", tstr
);
1409 l
= findLabel(tstr
);
1410 if (l
!= NULL
) fatal("can't redefine label as local: '%s'", tstr
);
1415 if (nextToken() != '=') fatal("'=' expected");
1416 if (nextToken() != TK_NUM
) fatal("number expected");
1418 if (nextToken() != ',') break;
1423 static void parseConst (int ext
, int gotext
) {
1426 if (nextToken() != TK_ID
) fatal("identifier expected");
1427 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1428 if (tstr
[0] == '.') fatal("invalid constant name: '%s'", tstr
);
1429 l
= findLabel(tstr
);
1432 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1433 if (l
->type
!= LB_CONST
) fatal("can't change existing extern label type: '%s'", tstr
);
1435 fatal("constant must be unique: '%s'", tstr
);
1444 if (nextToken() == '=') nextToken();
1445 if (token
!= TK_NUM
) fatal("constant must be numeric");
1448 if (ext
) l
->ext
= 1;
1449 if (gotext
) l
->ext
= -1;
1454 static char procname
[8192];
1455 static int proclocals
= 0;
1456 static int procargs
= 0;
1457 static int lastWasReturn
= 0;
1460 static void parseProc (int ext
) {
1461 LabelInfo
*l
, *a
= NULL
;
1465 if (nextToken() != TK_ID
) fatal("identifier expected");
1466 if (strcmp(tstr
, ".") == 0) fatal("invalid label name: '@@'");
1467 if (tstr
[0] == '.') fatal("invalid proc name: '%s'", tstr
);
1468 if (procname
[0]) fatal("unclosed proc: '%s'", procname
);
1469 strcpy(procname
, tstr
);
1472 freeLocalLabels(0); // vars and code
1474 l
= findLabel(tstr
);
1476 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate proc label: '%s'", tstr
);
1477 fixupLabelRefs(l
, pc
);
1483 if (ext
) l
->ext
= 1;
1487 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "arg") == 0 || strcmp(tstr
, "args") == 0)) {
1489 if (nextToken() != TK_ID
) fatal("identifier expected");
1490 if (tstr
[0] != '.') fatal("argument name must starts with '.'");
1491 l
= findLabel(tstr
);
1492 if (l
!= NULL
) fatal("duplicate argument: '%s'", l
->name
);
1497 if (nextToken() != ',') break;
1501 // -1: return address, -2: last arg
1502 for (spt
= -2, l
= a
; l
->type
== LB_SVAR
; l
= l
->next
) {
1508 spt
= -1; // first local
1509 while (token
== TK_LABELDEF
&& (strcmp(tstr
, "local") == 0 || strcmp(tstr
, "locals") == 0)) {
1511 if (nextToken() != TK_ID
) fatal("identifier expected");
1512 if (tstr
[0] != '.') fatal("local variable name must starts with '.'");
1513 l
= findLabel(tstr
);
1514 if (l
!= NULL
) fatal("duplicate local: '%s'", l
->name
);
1520 for (l
= a
; l
!= NULL
&& l
->type
== LB_SVAR
; l
= l
->next
) --(l
->value
);
1521 if (nextToken() != ',') break;
1525 if (proclocals
> 0) {
1529 setIntOperand(&op0
, -proclocals
);
1530 emitInstruction(VM_POP
, &op0
, NULL
, NULL
);
1535 static void parseEndP (void) {
1536 if (!procname
[0]) fatal("'endp' without 'proc'");
1537 if (nextToken() != TK_ID
) fatal("identifier expected");
1538 if (strcmp(procname
, tstr
) != 0) fatal("endp for '%s' in proc '%s'", tstr
, procname
);
1539 //if (!lastWasReturn) fatal("no 'return' in proc");
1546 static void doReturn (void) {
1547 OperandInfo op0
, op1
, op2
;
1549 if (!procname
[0]) fatal("'return' without 'proc'");
1551 op0
.var
= op1
.var
= op2
.var
= -1;
1552 if (hasOperand()) getOperand(&op2
, 0); // result
1553 setIntOperand(&op0
, proclocals
);
1554 setIntOperand(&op1
, procargs
);
1555 emitInstruction(VM_RET
, &op0
, &op1
, &op2
);
1559 static void parseLabel (int gotext
) {
1562 if (gotext
&& tstr
[0] == '.') fatal("can't declare local extern label: '%s'", tstr
);
1563 if (strcmp(tstr
, ".") == 0) {
1568 if (tstr
[0] != '.' && tstr
[0] != '@') {
1570 if (procname
[0]) fatal("you can not define non-special global labels inside proc ('%s')", procname
);
1572 freeLocalLabels(0); // vars and code
1574 if (tstr
[0] == '@') {
1577 while (*d
++) d
[-1] = d
[0];
1580 l
= findLabel(tstr
);
1583 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
1584 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
1586 if (l
->type
!= LB_CODE
|| l
->value
>= 0) fatal("duplicate label: '%s'", tstr
);
1587 fixupLabelRefs(l
, pc
);
1593 if (gotext
) l
->ext
= -1;
1598 static void parseDW (void) {
1600 LabelInfo
*l
= NULL
;
1602 if (token
== TK_ID
) {
1603 l
= findLabel(tstr
);
1609 //fatal("unknown label: '%s'", tstr);
1612 } else if (token
!= TK_NUM
) {
1613 fatal("number expected");
1615 //if (tint < -128 || tint > 255) fatal("bad value: %d", tint);
1616 emitByte(tint
&0xff);
1617 emitByte((tint
>>8)&0xff);
1619 addLabelRef(l
, pc
-2);
1620 addExternRef(l
, pc
-2, 2);
1621 if (l
->ext
>= 0 && l
->name
[0] != '.') {
1623 case LB_GVAR
: addGVarFixup(pc
-2); break;
1624 case LB_TVAR
: addTVarFixup(pc
-2); break;
1628 if (nextToken() != ',') break;
1634 static void parseAndPutString (int qch
) {
1636 int ch
= nextChar();
1638 //printf("[%c] [%c]\n", ch, qch);
1639 if (ch
== EOF
) fatal("unterminated string");
1641 if (ch
== qch
) break;
1646 if (ch
== EOF
) fatal("invalid escape");
1648 case 'a': emitByte('\a'); break;
1649 case 'b': emitByte('\b'); break;
1650 case 'e': emitByte('\x1b'); break;
1651 case 'f': emitByte('\f'); break;
1652 case 'n': emitByte('\n'); break;
1653 case 'r': emitByte('\r'); break;
1654 case 't': emitByte('\t'); break;
1655 case 'v': emitByte('\v'); break;
1656 case '"': case '\'': case '\\': case ' ': emitByte(ch
); break;
1658 n
= digit(nextChar(), 16);
1659 if (n
< 0) fatal("invalid hex escape");
1661 if (ch
== EOF
) fatal("invalid hex escape");
1662 if (digit(ch
, 16) >= 0) {
1663 n
= n
*16+digit(ch
, 16);
1669 default: fatal("invalid escape: '%c'", ch
);
1677 if (ch
== EOF
) return;
1692 static void parseDAscii (int zeroend
) {
1697 if (token
== TK_EOF
) break;
1699 case '"': tokenWantFileName
= 1; parseAndPutString(token
); tokenWantFileName
= 0; break;
1700 case '\'': tokenWantFileName
= 1; parseAndPutString(token
); tokenWantFileName
= 0; break;
1702 l
= findLabel(tstr
);
1704 if (l
== NULL
) fatal("unknown label: '%s'", tstr
);
1705 if (l
->type
== LB_CODE
) fatal("bad label type: '%s'", l
->name
);
1707 addExternRef(l
, pc
, 1);
1708 if (l
->ext
>= 0 && l
->name
[0] != '.') {
1710 case LB_GVAR
: addGVarFixup(pc
); break;
1711 case LB_TVAR
: addTVarFixup(pc
); break;
1717 if (tint
< -128 || tint
> 255) fatal("bad value: %d", tint
);
1718 emitByte(tint
&0xff);
1721 fatal("number expected");
1723 if (nextToken() != ',') break;
1725 if (zeroend
) emitByte(0);
1729 static void parsePublics (void) {
1734 if (token
== TK_LABELDEF
) {
1738 if (token
!= TK_ID
) fatal("identifier expected");
1739 if (tstr
[0] == '.') fatal("invalid label name: '%s'", tstr
);
1740 l
= findLabel(tstr
);
1749 if (nextToken() != ',') break;
1754 static void parseMacroDef (void) {
1758 int tpos
= 0, tsize
= 0;
1760 void addChar (int c
) {
1761 if (tpos
+1 > tsize
) {
1762 int newsz
= tsize
+1024;
1763 char *nn
= realloc(text
, newsz
);
1765 if (nn
== NULL
) fatal("out of memory");
1772 if (nextToken() != TK_ID
) fatal("macro name expected");
1773 if (tstr
[0] == '.') fatal("macros can't be local: '%s'", tstr
);
1774 for (mc
= macros
; mc
!= NULL
; mc
= mc
->next
) if (strcmp(mc
->name
, tstr
) == 0) fatal("macros can't be redefined: '%s'", tstr
);
1775 if ((mc
= calloc(1, sizeof(MacroDef
))) == NULL
) fatal("out of memory");
1777 if ((mc
->name
= strdup(tstr
)) == NULL
) fatal("out of memory");
1779 // now parse macro args
1782 skipSpaces(0); // no newlines allowed here
1788 if (nextToken() != TK_ID
) fatal("invalid macro argument definition");
1789 if (tstr
[0] == '.') fatal("macro argument name must not start with dot: '%s'", tstr
);
1790 addMacroArgDef(mc
, tstr
);
1793 if (c
!= ',') { ungetChar(c
); break; }
1796 // now parse macro text
1802 case EOF
: fatal("incomplete macro definition: '%s'", mc
->name
);
1803 case '\n': // check for macro end
1805 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1806 if (c
!= 'e') { ungetChar(c
); break; }
1807 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1808 if (c
!= 'n') { ungetChar(c
); addChar('e'); break; }
1809 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1810 if (c
!= 'd') { ungetChar(c
); addChar('e'); addChar('n'); break; }
1811 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1812 if (c
!= 'm') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); break; }
1813 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1814 if (c
!= 'a') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); break; }
1815 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1816 if (c
!= 'c') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); break; }
1817 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1818 if (c
!= 'r') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); break; }
1819 c
= nextChar(); if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1820 if (c
!= 'o') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); break; }
1822 if (c
== EOF
) goto macro_complete
;
1823 if (!isspace(c
) && c
!= ';' && c
!= '/') { ungetChar(c
); addChar('e'); addChar('n'); addChar('d'); addChar('m'); addChar('a'); addChar('c'); addChar('r'); addChar('o'); break; }
1825 skipSpaces(0); // no EOLs
1827 if (c
!= '\n') fatal("invalid endmacro: '%s' (%d)", mc
->name
, c
);
1828 goto macro_complete
;
1833 tokenWantFileName
= 1;
1836 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1838 if (c
== '"') break;
1841 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1845 tokenWantFileName
= 0;
1847 case '\'': // string
1849 tokenWantFileName
= 1;
1852 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1856 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1857 if (c
!= '\'') { ungetChar(c
); break; }
1861 tokenWantFileName
= 0;
1863 case ';': // comment
1868 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1869 if (c
== '\n') break;
1874 if (c1
== '/') goto one_line_comment
; // comment
1876 // multiline comment
1879 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1882 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1883 if (c
== '/') break;
1886 } else if (c1
== EOF
) {
1887 fatal("incomplete macro definition: '%s'", mc
->name
);
1905 static void parseMacroInvocation (MacroDef
*mc
) {
1906 MStrListItem
*args
= NULL
;
1908 if (mc
->argnames
!= NULL
) {
1909 nextToken(); // skip macro name
1910 // collect macro arguments
1911 for (MStrListItem
*a
= mc
->argnames
; a
!= NULL
; a
= a
->next
) {
1915 case TK_EOF
: fatal("macro argument expected: '%s'", mc
->name
);
1916 case TK_LABELDEF
: fatal("macro argument can't be label: '%s'", mc
->name
);
1917 case TK_NUM
: sprintf(tstr
, "%d", tint
); break;
1918 case TK_ID
: break; // ok
1921 tstr
[pos
++] = token
;
1922 tokenWantFileName
= 1;
1925 if (c
== EOF
) fatal("incomplete macro argument: '%s'", mc
->name
);
1926 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1928 if (c
== '"') break;
1931 if (c
== EOF
) fatal("incomplete macro argument: '%s'", mc
->name
);
1932 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1936 tokenWantFileName
= 0;
1939 case '\'': // string
1941 tstr
[pos
++] = token
;
1942 tokenWantFileName
= 1;
1945 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1946 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1950 if (c
== EOF
) fatal("incomplete macro definition: '%s'", mc
->name
);
1951 if (c
!= '\'') { ungetChar(c
); break; }
1952 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1956 tokenWantFileName
= 0;
1960 if (token
>= TK_VM_OPERATOR
) break;
1962 tstr
[pos
++] = token
;
1965 if (c
== EOF
) break;//fatal("incomplete macro definition: '%s'", mc->name);
1966 if (pos
>= MAX_TOKEN_LENGTH
) fatal("macro argument too long: '%s'", mc
->name
);
1967 //TODO: better comment parsing
1968 if (c
== ',' || c
== ';' || c
== '/' || c
== '\n') { ungetChar(c
); break; }
1971 while (isspace(tstr
[pos
-1])) --pos
;
1974 //fatal("invalid macro argument: '%s'", mc->name);
1976 //fprintf(stderr, "arg [%s]: [%s]\n", mc->argnames->macname, tstr);
1977 args
= addMacroArg(args
, mc
->argnames
->macname
, tstr
);
1978 if (a
->next
!= NULL
) {
1979 if (nextToken() != ',') fatal("macro argument expected: '%s'", mc
->name
);
1983 // arguments collected
1984 openMacro(mc
, args
);
1989 static void process (void) {
1991 LabelInfo
*l
= addLabel("retval");
1994 l
->value
= VM_VARS_SIZE
-1;
1996 memset(&labelTempBack
, 0, sizeof(LabelInfo
));
1997 memset(&labelTempFwd
, 0, sizeof(labelTempFwd
));
1999 labelTempBack
.type
= labelTempFwd
.type
= LB_CODE
;
2000 labelTempBack
.value
= labelTempFwd
.value
= -1;
2005 while (token
!= TK_EOF
) {
2008 if (token
== TK_LABELDEF
) {
2009 if (strcmp(tstr
, "extern") == 0) {
2010 if (gotext
) fatal("double 'extern'");
2015 // new label or operator
2016 if (strcmp(tstr
, "include") == 0) {
2019 } else if (strcmp(tstr
, "defloc") == 0) {
2020 if (gotext
) fatal("extern labels must not be locals");
2022 } else if (strcmp(tstr
, "defgvar") == 0) {
2023 parseVarList(LB_GVAR
, 0, gotext
);
2024 } else if (strcmp(tstr
, "defevar") == 0) { // extern global
2025 parseVarList(LB_GVAR
, -1, gotext
);
2026 } else if (strcmp(tstr
, "deftvar") == 0) {
2027 freeLocalLabels(1); // only vars
2028 vtloc
= VM_VARS_SIZE
;
2029 parseVarList(LB_TVAR
, 0, gotext
);
2030 } else if (strcmp(tstr
, "defetvar") == 0) {
2031 freeLocalLabels(1); // only vars
2032 vtloc
= VM_VARS_SIZE
;
2033 parseVarList(LB_TVAR
, -1, gotext
);
2034 } else if (strcmp(tstr
, "deflvar") == 0) {
2035 if (gotext
) fatal("extern labels must not be locals");
2036 parseVarList(LB_TVAR
, 1, gotext
);
2037 } else if (strcmp(tstr
, "public") == 0) {
2038 if (gotext
) fatal("invalid extern label declaration");
2040 } else if (strcmp(tstr
, "proc") == 0 || strcmp(tstr
, "eproc") == 0) {
2041 if (gotext
) fatal("invalid extern label declaration");
2042 parseProc(tstr
[0] == 'e');
2043 } else if (strcmp(tstr
, "endp") == 0) {
2044 if (gotext
) fatal("invalid extern label declaration");
2046 } else if (strcmp(tstr
, "const") == 0 || strcmp(tstr
, "econst") == 0) {
2047 parseConst(tstr
[0] == 'e', gotext
);
2048 } else if (strcmp(tstr
, "db") == 0) {
2049 if (gotext
) fatal("invalid extern label declaration");
2051 } else if (strcmp(tstr
, "dw") == 0) {
2052 if (gotext
) fatal("invalid extern label declaration");
2054 } else if (strcmp(tstr
, "da") == 0) {
2055 if (gotext
) fatal("invalid extern label declaration");
2057 } else if (strcmp(tstr
, "dz") == 0) {
2058 if (gotext
) fatal("invalid extern label declaration");
2060 } else if (strcmp(tstr
, "macro") == 0) {
2061 if (gotext
) fatal("macros can't be external");
2063 } else if (strcmp(tstr
, "used") == 0) {
2064 if (gotext
) fatal("'used' labels can't be external");
2066 } else if (strcmp(tstr
, "gvarbase") == 0) {
2067 if (gotext
) fatal("'gvarbase' can't be external");
2068 if (nextToken() != TK_NUM
) fatal("gvarbase: number expected");
2071 } else if (strcmp(tstr
, "tvarbase") == 0) {
2072 if (gotext
) fatal("'tvarbase' can't be external");
2073 if (nextToken() != TK_NUM
) fatal("tvarbase: number expected");
2085 if (token
!= TK_ID
) fatal("label declaration expected after 'extern'");
2086 if (tstr
[0] == '.') fatal("extern label can't be local: '%s'", tstr
);
2087 l
= findLabel(tstr
);
2089 if (l
->ext
>= 0) fatal("can't declare existing label as extern: '%s'", tstr
);
2090 if (l
->type
!= LB_CODE
) fatal("can't change existing extern label type: '%s'", tstr
);
2101 // check for macro invocation
2102 if (token
== TK_ID
) {
2103 for (MacroDef
*mc
= macros
; mc
!= NULL
; mc
= mc
->next
) {
2104 if (strcmp(mc
->name
, tstr
) == 0) {
2105 parseMacroInvocation(mc
);
2111 if (token
< TK_VM_OPERATOR
) {
2112 //fprintf(stderr, "%d: [%s]\n", token, tstr);
2113 fatal("mnemonics expected");
2116 if (opc
< 600) opc
-= TK_VM_OPERATOR
;
2204 if (procname
[0]) doReturn(); else doRet(opc
);
2210 doNoOperands(VM_PSH
);
2222 if (procname
[0]) fatal("'proc' without 'endp': '%s'", procname
);
2226 ////////////////////////////////////////////////////////////////////////////////
2227 static int cfWriteByte (FILE *fl
, int value
) {
2230 b
= (value
&0xff)^SECRET
;
2231 if (fwrite(&b
, 1, 1, fl
) != 1) return -1;
2236 static int cfWriteWord (FILE *fl
, int value
) {
2237 if (cfWriteByte(fl
, value
) != 0) return -1;
2238 if (cfWriteByte(fl
, value
>>8) != 0) return -1;
2243 static int cfWriteCode (FILE *fl
) {
2244 for (int f
= 0; f
< pc
; ++f
) if (cfWriteByte(fl
, vmcode
[f
]) != 0) return -1;
2249 static int cfWriteRels (FILE *fl
) {
2250 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) {
2251 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
2257 static int cfWritePublicLabels (FILE *fl
) {
2258 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2259 if (l
->ext
>= 0 && l
->name
[0]) {
2260 int len
= strlen(l
->name
);
2262 if (cfWriteByte(fl
, l
->type
|(l
->ext
>0?0x80:0)) != 0) return -1;
2267 if (cfWriteByte(fl
, l
->value
) != 0) return -1;
2272 if (cfWriteWord(fl
, l
->value
) != 0) return -1;
2278 if (len
> 255) len
= 255;
2279 if (cfWriteByte(fl
, len
) != 0) return -1;
2280 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
2287 static int cfWriteExtLabels (FILE *fl
) {
2288 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2289 if (l
->ext
< 0 && l
->name
[0] && l
->refs
!= NULL
) {
2290 int len
= strlen(l
->name
), rcnt
= 0;
2292 if (cfWriteByte(fl
, l
->type
) != 0) return -1;
2293 if (len
> 255) len
= 255;
2294 if (cfWriteByte(fl
, len
) != 0) return -1;
2295 for (int f
= 0; f
< len
; ++f
) if (cfWriteByte(fl
, (unsigned char)(l
->name
[f
])) != 0) return -1;
2297 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
2298 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
2299 for (LabelRefInfo
*r
= l
->refs
; r
!= NULL
; r
= r
->next
) {
2300 if (cfWriteByte(fl
, r
->size
) != 0) return -1;
2301 if (cfWriteWord(fl
, r
->pc
) != 0) return -1;
2309 static int cfWriteVarFixups (FILE *fl
, const VarFixup
*list
) {
2312 for (const VarFixup
*f
= list
; f
!= NULL
; f
= f
->next
) ++cnt
;
2313 if (cfWriteWord(fl
, cnt
) != 0) return -1;
2314 for (const VarFixup
*f
= list
; f
!= NULL
; f
= f
->next
) {
2315 if (cfWriteWord(fl
, f
->pc
) != 0) return -1;
2321 static int writeCodeFile (FILE *fl
) {
2322 static const char *sign
= "AVM2";
2323 int lcnt
= 0, elcnt
= 0;
2326 relrefs
= lrefRemoveDups(relrefs
);
2327 for (LabelRefInfo
*r
= relrefs
; r
!= NULL
; r
= r
->next
) ++rcnt
;
2328 for (LabelInfo
*l
= labels
; l
!= NULL
; l
= l
->next
) {
2330 if (l
->ext
>= 0) ++lcnt
;
2332 l
->refs
= lrefRemoveDups(l
->refs
);
2333 if (l
->refs
!= NULL
) ++elcnt
;
2337 fprintf(stderr
, "%d bytes of code, %d public labels, %d fixups, %d externs; maxgvar: %d, maxtvar: %d\n", pc
, lcnt
, rcnt
, elcnt
, vglast
, vtlast
);
2339 if (fwrite(sign
, 4, 1, fl
) != 1) return -1;
2341 if (cfWriteWord(fl
, pc
) != 0) return -1;
2343 if (cfWriteWord(fl
, rcnt
) != 0) return -1;
2344 // number of extern labels
2345 if (cfWriteWord(fl
, elcnt
) != 0) return -1;
2347 if (cfWriteWord(fl
, lcnt
) != 0) return -1;
2349 if (cfWriteWord(fl
, vglast
) != 0) return -1;
2350 // last used thread local
2351 if (cfWriteWord(fl
, vtlast
) != 0) return -1;
2353 if (cfWriteCode(fl
) != 0) return -1;
2354 if (cfWriteRels(fl
) != 0) return -1;
2355 if (cfWriteExtLabels(fl
) != 0) return -1;
2356 if (cfWritePublicLabels(fl
) != 0) return -1;
2357 if (cfWriteVarFixups(fl
, gvfixes
) != 0) return -1;
2358 if (cfWriteVarFixups(fl
, tvfixes
) != 0) return -1;
2364 ////////////////////////////////////////////////////////////////////////////////
2366 # include "cmdline.c"
2370 ////////////////////////////////////////////////////////////////////////////////
2371 int main (int argc
, char *argv
[]) {
2378 for (int f
= 1; f
< argc
; ++f
) {
2379 if (strcmp(argv
[f
], "-Wno") == 0) {
2381 for (int c
= f
+1; c
< argc
; ++c
) argv
[c
-1] = argv
[c
];
2382 argv
[--argc
] = NULL
;
2389 fprintf(stderr
, "usage: awasm infile outfile\n");
2393 //while (nextToken() != TK_EOF) printf("%d [%s] %d\n", token, tstr, tint); return 0;
2395 while (ictx
!= NULL
) closeFile();
2396 freeLocalLabels(0); // vars and code
2398 //if (argc > 3) dumpGlobalVars(argv[3]);
2400 FILE *fo
= fopen(argv
[2], "wb");
2403 if (fo
== NULL
) { fprintf(stderr
, "FATAL: can't create output file: '%s'\n", argv
[2]); return 1; }
2404 res
= writeCodeFile(fo
);
2405 if (fclose(fo
) != 0) res
= -1;
2407 fprintf(stderr
, "FATAL: error writing output file: '%s'\n", argv
[2]);
2413 freeLabelRefList(relrefs
);