2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // TOKEN.C - Token Handling
4 // Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source Utilised with the Kind Permission of Landon Dyer
15 #define DECL_KW // Declare keyword arrays
16 #define DEF_KW // Declare keyword values
17 #include "kwtab.h" // Incl generated keyword tables & defs
19 int lnsave; // 1; strcpy() text of current line
20 int curlineno; // Current line number
21 int totlines; // Total # of lines
22 int mjump_align = 0; // mjump alignment flag
23 char lntag; // Line tag
24 char * curfname; // Current filename
25 char tolowertab[128]; // Uppercase ==> lowercase
26 char hextab[128]; // Table of hex values
27 char dotxtab[128]; // Table for ".b", ".s", etc.
28 char irbuf[LNSIZ]; // Text for .rept block line
29 char lnbuf[LNSIZ]; // Text of current line
30 WORD filecount; // Unique file number counter
31 WORD cfileno; // Current file number
32 TOKEN * tok; // Ptr to current token
33 TOKEN * etok; // Ptr past last token in tokbuf[]
34 TOKEN tokeol[1] = {EOL}; // Bailout end-of-line token
36 // File record, used to maintain a list of every include file ever visited
37 #define FILEREC struct _filerec
47 INOBJ * cur_inobj; // Ptr current input obj (IFILE/IMACRO)
48 static INOBJ * f_inobj; // Ptr list of free INOBJs
49 static IFILE * f_ifile; // Ptr list of free IFILEs
50 static IMACRO * f_imacro; // Ptr list of free IMACROs
52 static TOKEN tokbuf[TOKBUFSIZE]; // Token buffer (stack-like, all files)
55 ILLEG, ILLEG, ILLEG, ILLEG, // NUL SOH STX ETX
56 ILLEG, ILLEG, ILLEG, ILLEG, // EOT ENQ ACK BEL
57 ILLEG, WHITE, ILLEG, ILLEG, // BS HT LF VT
58 WHITE, ILLEG, ILLEG, ILLEG, // FF CR SO SI
60 ILLEG, ILLEG, ILLEG, ILLEG, // DLE DC1 DC2 DC3
61 ILLEG, ILLEG, ILLEG, ILLEG, // DC4 NAK SYN ETB
62 ILLEG, ILLEG, ILLEG, ILLEG, // CAN EM SUB ESC
63 ILLEG, ILLEG, ILLEG, ILLEG, // FS GS RS US
65 WHITE, MULTX, MULTX, SELF, // SP ! " #
66 MULTX+CTSYM, MULTX, SELF, MULTX, // $ % & '
67 SELF, SELF, SELF, SELF, // ( ) * +
68 SELF, SELF, STSYM, SELF, // , - . /
70 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 0 1
71 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 2 3
72 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 4 5
73 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 6 7
74 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 8 9
76 MULTX, MULTX, MULTX, STSYM+CTSYM, // < = > ?
78 MULTX, STSYM+CTSYM+HDIGIT, // @ A
79 (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // B C
80 STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // D E
81 STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // F G
82 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // H I J K
83 (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // L M N O
85 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // P Q R S
86 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // T U V W
87 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // X Y Z [
88 SELF, SELF, MULTX, STSYM+CTSYM, // \ ] ^ _
90 ILLEG, STSYM+CTSYM+HDIGIT, // ` a
91 (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // b c
92 STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // d e
93 STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // f g
94 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // h i j k
95 (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // l m n o
97 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // p q r s
98 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // t u v w
99 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // x y z {
100 SELF, SELF, SELF, ILLEG // | } ~ DEL
103 // Names of registers
104 static char * regname[] = {
105 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
106 "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
107 "pc", "ssp", "usp", "sr", "ccr"
110 static char * riscregname[] = {
111 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
112 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
113 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
114 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
118 // --- Make `fnum' the Current `curfname' ----------------------------------------------------------
121 void setfnum(WORD fnum)
125 for(fr = filerec; fr != NULL && fnum--; fr = fr->frec_next)
129 curfname = "(*top*)";
131 curfname = fr->frec_name;
135 // --- Allocate an IFILE or IMACRO -----------------------------------------------------------------
138 INOBJ * a_inobj(int typ)
144 // Allocate and initialize INOBJ first
146 inobj = (INOBJ *)amem((LONG)sizeof(INOBJ));
149 f_inobj = f_inobj->in_link;
153 case SRC_IFILE: // Alloc and init an IFILE
155 ifile = (IFILE *)amem((LONG)sizeof(IFILE));
158 f_ifile = f_ifile->if_link;
160 inobj->inobj.ifile = ifile;
162 case SRC_IMACRO: // Alloc and init an IMACRO
164 imacro = (IMACRO *)amem((LONG)sizeof(IMACRO));
167 f_imacro = f_imacro->im_link;
169 inobj->inobj.imacro = imacro;
171 case SRC_IREPT: // Alloc and init an IREPT
172 inobj->inobj.irept = (IREPT *)amem((LONG)sizeof(IREPT));
173 DEBUG printf("alloc IREPT\n");
177 // Install INOBJ on top of input stack
178 inobj->in_ifent = ifent; // Record .if context on entry
179 inobj->in_type = (WORD)typ;
180 inobj->in_otok = tok;
181 inobj->in_etok = etok;
182 inobj->in_link = cur_inobj;
189 // -------------------------------------------------------------------------------------------------
190 // Perform macro substitution from 'orig' to 'dest'. Return OK or some error.
191 // A macro reference is in one of two forms:
192 // \name <non-name-character>
194 // A doubled backslash (\\) is compressed to a single backslash (\).
195 // Argument definitions have been pre-tokenized, so we have to turn them back into text. This
196 // means that numbers, in particular, become hex, regardless of their representation when the macro
197 // was invoked. This is a hack.
198 // A label may appear at the beginning of the line:
199 // :<name><whitespace>
200 // (the colon must be in the first column). These labels are stripped before macro expansion takes
202 // -------------------------------------------------------------------------------------------------
205 int mexpand(char * src, char * dest, int destsiz)
209 char *dst; // Next dest slot
210 char *edst; // End+1 of dest buffer
212 int questmark; // \? for testing argument existence
214 char mname[128]; // Assume max size of a formal arg name
218 char numbuf[20]; // Buffer for text of CONSTs
220 imacro = cur_inobj->inobj.imacro;
221 macnum = (int)(imacro->im_macro->sattr);
225 edst = dest + destsiz;
227 // Check for (and skip over) any "label" on the line
230 while(*s != EOS && !(chrtab[*s] & WHITE)) ++s;
231 if(*s != EOS) ++s; // Skip first whitespace
234 // Expand the rest of the line
236 if(*s != '\\') { // Copy single character
240 } else { // Do macro expansion
245 case '\\': // \\, \ (collapse to single backslash)
250 case '?': // \? <macro> set `questmark' flag
254 case '#': // \#, number of arguments
255 sprintf(numbuf, "%d", (int)imacro->im_nargs);
257 case '!': // \! size suffix supplied on invocation
258 switch((int)imacro->im_siz) {
259 case SIZN: d = ""; break;
260 case SIZB: d = ".b"; break;
261 case SIZW: d = ".w"; break;
262 case SIZL: d = ".l"; break;
265 case '~': // ==> unique label string Mnnnn...
266 sprintf(numbuf, "M%ld", curuniq);
276 if(dst >= edst) goto overflow;
280 return(error("missing argument name"));
283 // \n ==> argument number 'n', 0..9
284 if(chrtab[*s] & DIGIT) {
291 // Get argument name: \name, \{name}
293 if(*s != '{') { // \foo
296 while(chrtab[*s] & CTSYM);
298 for(++s; *s != EOS && *s != '}';)
300 if(*s != '}') return(error("missing '}'"));
305 // Lookup the argument and copy its (string) value into the destination string
306 DEBUG printf("mname='%s'\n", mname);
307 if((arg = lookup(mname, MACARG, macnum)) == NULL)
308 return(errors("undefined argument: '%s'", mname));
310 // Convert a string of tokens (terminated with EOL) back into text. If an argument
311 // is out of range (not specified in the macro invocation) then it is ignored.
312 i = (int)arg->svalue;
316 DEBUG printf("~argnumber=%d\n", i);
319 if(i < imacro->im_nargs)
323 // 0 if the argument is empty or non-existant,
324 // 1 if the argument is not empty
326 if(tk == NULL || *tk == EOL)
330 *dst++ = (char)(questmark + '0');
334 if(tk != NULL) // arg# is in range, so expand it
336 // Reverse-translation from a token number to a string. This is a hack.
337 // It might be better table-driven.
339 if((*tk >= KW_D0) && !rdsp && !rgpu) {
340 d = regname[(int)*tk++ - KW_D0];
342 } else if((*tk >= KW_R0) && (*tk <= KW_R31)) {
343 d = riscregname[(int)*tk++ - KW_R0];
368 sprintf(numbuf, "$%lx", (LONG)*tk++);
428 *dst++ = (char)*(tk-1);
432 // If 'd' != NULL, copy string to destination
437 DEBUG printf("d='%s'\n", d);
454 return(fatal("line too long as a result of macro expansion"));
458 // --- Get Next Line of Text from a Macro ----------------------------------------------------------
465 unsigned source_addr;
467 imacro = cur_inobj->inobj.imacro;
468 strp = imacro->im_nextln;
470 if(strp == NULL) // End-of-macro
473 imacro->im_nextln = (LONG *)*strp;
474 mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
476 if(!strcmp(imacro->im_macro->sname, "mjump") && !mjump_align) {
477 // if we need to adjust the alignment of the jump source address to meet the rules of
478 // gpu main execution we need to skip the first nop of the macro. This is simpler than
479 // trying to insert nop's mid macro.
480 source_addr = (orgactive) ? orgaddr : sloc;
482 if(source_addr % 4) {
483 strp = imacro->im_nextln;
484 if(strp == NULL) return(NULL);
485 imacro->im_nextln = (LONG *)*strp;
486 mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
491 return(imacro->im_lnbuf);
495 // --- Get Next Line of Text from a Repeat Block ---------------------------------------------------
503 irept = cur_inobj->inobj.irept;
504 strp = irept->ir_nextln; // initial null
506 // Do repeat at end of .rept block's string list
508 DEBUG printf("back-to-top-of-repeat-block count=%d\n", (int)irept->ir_count);
509 irept->ir_nextln = irept->ir_firstln; // copy first line
510 if(irept->ir_count-- == 0) {
511 DEBUG printf("end-repeat-block\n");
514 strp = irept->ir_nextln; //strp
517 strcpy(irbuf, (char*)(irept->ir_nextln + 1));
519 DEBUG printf("repeat line='%s'\n", irbuf);
520 irept->ir_nextln = (LONG *)*strp;
526 // --- Include a Source File used at the Root, and for ".include" Files ----------------------------
529 int include(int handle, char * fname)
535 if(verb_flag) printf("[Including: %s]\n", fname); // Verbose mode
537 // Alloc and initialize include-descriptors
538 inobj = a_inobj(SRC_IFILE);
539 ifile = inobj->inobj.ifile;
541 ifile->ifhandle = handle; // Setup file handle
542 ifile->ifind = ifile->ifcnt = 0; // Setup buffer indices
543 ifile->ifoldlineno = curlineno; // Save old line number
544 ifile->ifoldfname = curfname; // Save old filename
545 ifile->ifno = cfileno; // Save old file number
546 cfileno = ++filecount; // Compute new file number
547 curfname = nstring(fname); // Set current filename (alloc storage)
548 curlineno = 0; // Start on line zero
550 // Add another file to the file-record
551 fr = (FILEREC *)amem((LONG)sizeof(FILEREC));
552 fr->frec_next = NULL;
553 fr->frec_name = curfname;
555 filerec = fr; // Add first filerec
557 last_fr->frec_next = fr; // Append to list of filerecs
564 // --- Initialize Tokenizer ------------------------------------------------------------------------
567 void init_token(void)
570 char *htab = "0123456789abcdefABCDEF"; // Hex character table
572 lnsave = 0; // Don't save lines
573 curfname = ""; // No file, empty filename
574 filecount = (WORD)-1;
575 cfileno = (WORD)-1; // cfileno gets bumped to 0
587 // Initialize hex, "dot" and tolower tables
588 for(i = 0; i < 128; ++i) {
591 tolowertab[i] = (char)i;
593 for(i = 0; htab[i] != EOS; ++i)
594 hextab[htab[i]] = (char)((i < 16) ? i : i - 6);
595 for(i = 'A'; i <= 'Z'; ++i)
596 tolowertab[i] |= 0x20;
598 // These characters are legal immediately after a period
599 dotxtab['b'] = DOTB; // .b .B .s .S
603 dotxtab['w'] = DOTW; // .w .W
605 dotxtab['l'] = DOTL; // .l .L
607 dotxtab['I'] = DOTI; // .l .L
612 // --- Pop the Current Input Level -----------------------------------------------------------------
623 // Pop IFENT levels until we reach the conditional assembly context we were at when the
624 // input object was entered.
625 while(ifent != inobj->in_ifent)
628 tok = inobj->in_otok; // Restore tok and otok
629 etok = inobj->in_etok;
631 switch(inobj->in_type) {
632 case SRC_IFILE: // Pop and release an IFILE
634 printf("[Leaving: %s]\n", curfname);
635 ifile = inobj->inobj.ifile;
636 ifile->if_link = f_ifile;
638 close(ifile->ifhandle); // Close source file
639 curfname = ifile->ifoldfname; // Set current filename
640 curlineno = ifile->ifoldlineno; // Set current line#
641 DEBUG printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno);
642 cfileno = ifile->ifno; // Restore current file number
644 case SRC_IMACRO: // Pop and release an IMACRO
645 imacro = inobj->inobj.imacro;
646 imacro->im_link = f_imacro;
649 case SRC_IREPT: // Pop and release an IREPT
650 DEBUG printf("dealloc IREPT\n");
651 p = inobj->inobj.irept->ir_firstln;
659 cur_inobj = inobj->in_link;
660 inobj->in_link = f_inobj;
668 // --- Get line from file into buf, return NULL on EOF or ptr to the start of a null-term line -----
678 readamt = -1; // 0 if last read() yeilded 0 bytes
679 fl = cur_inobj->inobj.ifile;
682 // Scan for next end-of-line; handle stupid text formats by treating \r\n the same as \n.
683 // (lone '\r' at end of buffer means we have to check for '\n').
686 d = &fl->ifbuf[fl->ifind];
688 for(p = d; i < j; ++i, ++p) {
689 if(*p == '\r' || *p == '\n') {
693 break; // Look for '\n' to eat
694 } else if(p[1] == '\n') {
707 // Handle hanging lines by ignoring them (Input file is exhausted, no \r or \n on last line)
708 if(!readamt && fl->ifcnt) {
714 // Truncate and return absurdly long lines.
715 if(fl->ifcnt >= QUANTUM) {
716 fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0';
718 return(&fl->ifbuf[fl->ifind]);
721 // Relocate what's left of a line to the beginning of the buffer, and read some more of the
722 // file in; return NULL if the buffer's empty and on EOF.
724 p = &fl->ifbuf[fl->ifind];
725 d = &fl->ifbuf[fl->ifcnt & 1];
726 for(i = 0; i < fl->ifcnt; ++i)
728 fl->ifind = fl->ifcnt & 1;
731 if((readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM)) < 0)
734 if((fl->ifcnt += readamt) == 0)
740 // --- Tokenize a Line -----------------------------------------------------------------------------
745 char *ln = NULL; // Ptr to current position in line
746 char *p; // Random character ptr
747 TOKEN *tk; // Token-deposit ptr
748 int state = 0; // State for keyword detector
749 int j = 0; // Var for keyword detector
750 char c; // Random char
751 VALUE v; // Random value
752 char *nullspot = NULL; // Spot to clobber for SYMBOL terminatn
753 int stuffnull; // 1:terminate SYMBOL '\0' at *nullspot
758 if(cur_inobj == NULL) // Return EOF if input stack is empty
761 // Get another line of input from the current input source: a file, a macro, or a repeat-block
762 switch(cur_inobj->in_type) {
765 // o bump source line number;
766 // o tag the listing-line with a space;
767 // o kludge lines generated by Alcyon C.
769 if((ln = getln()) == NULL) {
770 fpop(); // Pop input level
771 goto retry; // Try for more lines
773 ++curlineno; // Bump line number
776 // AS68 compatibility, throw away all lines starting with back-quotes, tildes, or '*'
777 // On other lines, turn the first '*' into a semi-colon.
778 if(*ln == '`' || *ln == '~' || *ln == '*') *ln = ';';
779 else for(p = ln; *p != EOS; ++p) {
789 // o Handle end-of-macro;
790 // o tag the listing-line with an at (@) sign.
792 if((ln = getmln()) == NULL) {
793 exitmac(); // Exit macro (pop args, do fpop(), etc)
794 goto retry; // Try for more lines...
800 // o Handle end-of-repeat-block;
801 // o tag the listing-line with a pound (#) sign.
803 if((ln = getrln()) == NULL) {
811 // Save text of the line. We only do this during listings and within macro-type blocks,
812 // since it is expensive to unconditionally copy every line.
813 if(lnsave) strcpy(lnbuf, ln);
815 // General house-keeping
816 tok = tokeol; // Set "tok" to EOL in case of error
817 tk = etok; // Reset token ptr
818 stuffnull = 0; // Don't stuff nulls
819 ++totlines; // Bump total #lines assembled
821 // See if the entire line is a comment. This is a win if the programmer puts in lots of comments
822 if(*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln+1) == '/'))) goto goteol;
824 // Main tokenization loop;
825 // o skip whitespace;
826 // o handle end-of-line;
828 // o handle single-character tokens (operators, etc.);
829 // o handle multiple-character tokens (constants, strings, etc.).
831 // Skip whitespace, handle EOL
832 while((int)chrtab[*ln] & WHITE)
835 // Handle EOL, comment with ';'
836 if(*ln == EOS || *ln == ';'|| ((*ln == '/') && (*(ln+1) == '/')))
839 // Handle start of symbol. Symbols are null-terminated in place. The termination is
840 // always one symbol behind, since there may be no place for a null in the case that
841 // an operator immediately follows the name.
844 if(stuffnull) // Terminate old symbol
846 v = 0; // Assume no DOT attrib follows symbol
848 p = nullspot = ln++; // Nullspot -> start of this symbol
850 // Find end of symbol (and compute its length)
851 for(j = 1; (int)chrtab[*ln] & CTSYM; ++j)
854 // Handle "DOT" special forms (like ".b") that follow a normal symbol or keyword:
856 *ln++ = EOS; // Terminate symbol
857 stuffnull = 0; // And never try it again
859 // Character following the `.' must have a DOT attribute, and the chararacter after
860 // THAT one must not have a start-symbol attribute (to prevent symbols that look
861 // like, for example, "zingo.barf", which might be a good idea anyway....)
862 if((((int)chrtab[*ln] & DOT) == 0) || ((int)dotxtab[*ln] <= 0))
863 return(error("[bwsl] must follow `.' in symbol"));
864 v = (VALUE)dotxtab[*ln++];
865 if((int)chrtab[*ln] & CTSYM)
866 return(error("misuse of `.', not allowed in symbols"));
869 // If the symbol is small, check to see if it's really the name of a register.
871 for(state = 0; state >= 0;) {
872 j = (int)tolowertab[*p++];
874 if(kwcheck[j] != state) {
879 if(*p == EOS || p == ln) {
890 //make j = -1 if time, date etc with no preceeding ^^
891 //defined, referenced, streq, macdef, date and time
894 case 113: // referenced
903 if(j < 0 || state < 0) {
905 *tk++ = (TOKEN)nullspot;
911 if(v) // Record attribute token (if any)
914 if(stuffnull) // Arrange for string termination
919 // Handle identity tokens
925 // Handle multiple-character tokens
934 case '\'': // 'string'
935 case '\"': // "string"
940 for(p = ln; *ln != EOS && *ln != c1;) {
945 return(error("unterminated string"));
974 warn("bad backslash code in string");
982 return(error("unterminated string"));
985 case '$': // $, hex constant
986 if((int)chrtab[*ln] & HDIGIT) {
988 while((int)hextab[*ln] >= 0)
989 v = (v << 4) + (int)hextab[*ln++];
991 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
992 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
993 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
999 case '<': // < or << or <> or <=
1017 case ':': // : or ::
1023 case '=': // = or ==
1029 case '>': // > or >> or >=
1043 case '%': // % or binary constant
1044 if(*ln < '0' || *ln > '1') {
1049 while(*ln >= '0' && *ln <= '1')
1050 v = (v << 1) + *ln++ - '0';
1052 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1053 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1054 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1059 case '@': // @ or octal constant
1060 if(*ln < '0' || *ln > '7') {
1065 while(*ln >= '0' && *ln <= '7')
1066 v = (v << 3) + *ln++ - '0';
1068 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1069 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1070 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1075 case '^': // ^ or ^^ <operator-name>
1081 if(((int)chrtab[*++ln] & STSYM) == 0) {
1082 error("invalid symbol following ^^");
1087 while((int)chrtab[*ln] & CTSYM)
1090 for(state = 0; state >= 0;) {
1091 // Get char, convert to lowercase
1093 if(j >= 'A' && j <= 'Z')
1097 if(kwcheck[j] != state) {
1102 if(*p == EOS || p == ln) {
1109 if(j < 0 || state < 0) {
1110 error("unknown symbol following ^^");
1117 interror(2); // Bad MULTX entry in chrtab
1123 // Handle decimal constant
1126 while((int)chrtab[*ln] & DIGIT)
1127 v = (v * 10) + *ln++ - '0';
1129 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1130 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1131 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1138 // Handle illegal character
1139 return(error("illegal character"));
1142 // Terminate line of tokens and return "success."
1146 tok = etok; // Set tok to beginning of line
1147 if(stuffnull) // Terminate last SYMBOL
1155 // -------------------------------------------------------------------------------------------------
1156 // .GOTO <label> goto directive
1158 // The label is searched for starting from the first line of the current, enclosing macro
1159 // definition. If no enclosing macro exists, an error is generated.
1161 // A label is of the form:
1163 // :<name><whitespace>
1165 // The colon must appear in column 1. The label is stripped prior to macro expansion, and is NOT
1166 // subject to macro expansion. The whitespace may also be EOL.
1167 // -------------------------------------------------------------------------------------------------
1170 //int d_goto(WORD siz) {
1173 char *sym; // Label to search for
1174 LONG *defln; // Macro definition strings
1175 char *s1; // Temps for string comparison
1177 IMACRO *imacro; // Macro invocation block
1179 // Setup for the search
1180 if(*tok != SYMBOL) return(error("missing label"));
1181 sym = (char *)tok[1];
1184 if(cur_inobj->in_type != SRC_IMACRO) return(error("goto not in macro"));
1185 imacro = cur_inobj->inobj.imacro;
1186 defln = (LONG *)imacro->im_macro->svalue;
1188 // Find the label, starting with the first line.
1189 for(; defln != NULL; defln = (LONG *)*defln)
1190 if(*(char *)(defln + 1) == ':') {
1191 // Compare names (sleazo string compare)
1193 s2 = (char *)(defln + 1) + 1;
1195 if(*s1 == EOS) break;
1201 // Found the label, set new macro next-line and return.
1202 if((*s2 == EOS) || ((int)chrtab[*s2] & WHITE)) {
1203 imacro->im_nextln = defln;
1208 return(error("goto label not found"));