1 ////////////////////////////////////////////////////////////////////////////////////////////////////
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
14 #define DECL_KW // Declare keyword arrays
15 #define DEF_KW // Declare keyword values
16 #include "kwtab.h" // Incl generated keyword tables & defs
18 int lnsave; // 1; strcpy() text of current line
19 int curlineno; // Current line number
20 int totlines; // Total # of lines
21 int mjump_align = 0; // mjump alignment flag
22 char lntag; // Line tag
23 char *curfname; // Current filename
24 char tolowertab[128]; // Uppercase ==> lowercase
25 char hextab[128]; // Table of hex values
26 char dotxtab[128]; // Table for ".b", ".s", etc.
27 char irbuf[LNSIZ]; // Text for .rept block line
28 char lnbuf[LNSIZ]; // Text of current line
29 WORD filecount; // Unique file number counter
30 WORD cfileno; // Current file number
31 TOKEN *tok; // Ptr to current token
32 TOKEN *etok; // Ptr past last token in tokbuf[]
33 TOKEN tokeol[1] = {EOL}; // Bailout end-of-line token
35 // File record, used to maintain a list of every include file ever visited
36 #define FILEREC struct _filerec
45 INOBJ *cur_inobj; // Ptr current input obj (IFILE/IMACRO)
46 static INOBJ *f_inobj; // Ptr list of free INOBJs
47 static IFILE *f_ifile; // Ptr list of free IFILEs
48 static IMACRO *f_imacro; // Ptr list of free IMACROs
50 static TOKEN tokbuf[TOKBUFSIZE]; // Token buffer (stack-like, all files)
53 ILLEG, ILLEG, ILLEG, ILLEG, // NUL SOH STX ETX
54 ILLEG, ILLEG, ILLEG, ILLEG, // EOT ENQ ACK BEL
55 ILLEG, WHITE, ILLEG, ILLEG, // BS HT LF VT
56 WHITE, ILLEG, ILLEG, ILLEG, // FF CR SO SI
58 ILLEG, ILLEG, ILLEG, ILLEG, // DLE DC1 DC2 DC3
59 ILLEG, ILLEG, ILLEG, ILLEG, // DC4 NAK SYN ETB
60 ILLEG, ILLEG, ILLEG, ILLEG, // CAN EM SUB ESC
61 ILLEG, ILLEG, ILLEG, ILLEG, // FS GS RS US
63 WHITE, MULTX, MULTX, SELF, // SP ! " #
64 MULTX+CTSYM, MULTX, SELF, MULTX, // $ % & '
65 SELF, SELF, SELF, SELF, // ( ) * +
66 SELF, SELF, STSYM, SELF, // , - . /
68 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 0 1
69 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 2 3
70 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 4 5
71 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 6 7
72 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 8 9
74 MULTX, MULTX, MULTX, STSYM+CTSYM, // < = > ?
76 MULTX, STSYM+CTSYM+HDIGIT, // @ A
77 (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // B C
78 STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // D E
79 STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // F G
80 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // H I J K
81 (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // L M N O
83 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // P Q R S
84 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // T U V W
85 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // X Y Z [
86 SELF, SELF, MULTX, STSYM+CTSYM, // \ ] ^ _
88 ILLEG, STSYM+CTSYM+HDIGIT, // ` a
89 (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // b c
90 STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // d e
91 STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // f g
92 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // h i j k
93 (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // l m n o
95 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // p q r s
96 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // t u v w
97 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // x y z {
98 SELF, SELF, SELF, ILLEG // | } ~ DEL
101 // Names of registers
102 static char *regname[] = {
103 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
104 "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
105 "pc", "ssp", "usp", "sr", "ccr"
108 static char *riscregname[] = {
109 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
110 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
111 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
112 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
116 // --- Make `fnum' the Current `curfname' ----------------------------------------------------------
119 void setfnum(WORD fnum) {
122 for(fr = filerec; fr != NULL && fnum--; fr = fr->frec_next)
126 curfname = "(*top*)";
128 curfname = fr->frec_name;
132 // --- Allocate an IFILE or IMACRO -----------------------------------------------------------------
135 INOBJ *a_inobj(int typ) {
140 // Allocate and initialize INOBJ first
142 inobj = (INOBJ *)amem((LONG)sizeof(INOBJ));
145 f_inobj = f_inobj->in_link;
149 case SRC_IFILE: // Alloc and init an IFILE
151 ifile = (IFILE *)amem((LONG)sizeof(IFILE));
154 f_ifile = f_ifile->if_link;
156 inobj->inobj.ifile = ifile;
158 case SRC_IMACRO: // Alloc and init an IMACRO
160 imacro = (IMACRO *)amem((LONG)sizeof(IMACRO));
163 f_imacro = f_imacro->im_link;
165 inobj->inobj.imacro = imacro;
167 case SRC_IREPT: // Alloc and init an IREPT
168 inobj->inobj.irept = (IREPT *)amem((LONG)sizeof(IREPT));
169 DEBUG printf("alloc IREPT\n");
173 // Install INOBJ on top of input stack
174 inobj->in_ifent = ifent; // Record .if context on entry
175 inobj->in_type = (WORD)typ;
176 inobj->in_otok = tok;
177 inobj->in_etok = etok;
178 inobj->in_link = cur_inobj;
185 // -------------------------------------------------------------------------------------------------
186 // Perform macro substitution from 'orig' to 'dest'. Return OK or some error.
187 // A macro reference is in one of two forms:
188 // \name <non-name-character>
190 // A doubled backslash (\\) is compressed to a single backslash (\).
191 // Argument definitions have been pre-tokenized, so we have to turn them back into text. This
192 // means that numbers, in particular, become hex, regardless of their representation when the macro
193 // was invoked. This is a hack.
194 // A label may appear at the beginning of the line:
195 // :<name><whitespace>
196 // (the colon must be in the first column). These labels are stripped before macro expansion takes
198 // -------------------------------------------------------------------------------------------------
201 int mexpand(char *src, char *dest, int destsiz) {
204 char *dst; // Next dest slot
205 char *edst; // End+1 of dest buffer
207 int questmark; // \? for testing argument existence
209 char mname[128]; // Assume max size of a formal arg name
213 char numbuf[20]; // Buffer for text of CONSTs
215 imacro = cur_inobj->inobj.imacro;
216 macnum = (int)(imacro->im_macro->sattr);
220 edst = dest + destsiz;
222 // Check for (and skip over) any "label" on the line
225 while(*s != EOS && !(chrtab[*s] & WHITE)) ++s;
226 if(*s != EOS) ++s; // Skip first whitespace
229 // Expand the rest of the line
231 if(*s != '\\') { // Copy single character
235 } else { // Do macro expansion
240 case '\\': // \\, \ (collapse to single backslash)
245 case '?': // \? <macro> set `questmark' flag
249 case '#': // \#, number of arguments
250 sprintf(numbuf, "%d", (int)imacro->im_nargs);
252 case '!': // \! size suffix supplied on invocation
253 switch((int)imacro->im_siz) {
254 case SIZN: d = ""; break;
255 case SIZB: d = ".b"; break;
256 case SIZW: d = ".w"; break;
257 case SIZL: d = ".l"; break;
260 case '~': // ==> unique label string Mnnnn...
261 sprintf(numbuf, "M%ld", curuniq);
271 if(dst >= edst) goto overflow;
275 return(error("missing argument name"));
278 // \n ==> argument number 'n', 0..9
279 if(chrtab[*s] & DIGIT) {
286 // Get argument name: \name, \{name}
288 if(*s != '{') { // \foo
291 while(chrtab[*s] & CTSYM);
293 for(++s; *s != EOS && *s != '}';)
295 if(*s != '}') return(error("missing '}'"));
300 // Lookup the argument and copy its (string) value into the destination string
301 DEBUG printf("mname='%s'\n", mname);
302 if((arg = lookup(mname, MACARG, macnum)) == NULL)
303 return(errors("undefined argument: '%s'", mname));
305 // Convert a string of tokens (terminated with EOL) back into text. If an argument
306 // is out of range (not specified in the macro invocation) then it is ignored.
307 i = (int)arg->svalue;
311 DEBUG printf("~argnumber=%d\n", i);
314 if(i < imacro->im_nargs)
318 // 0 if the argument is empty or non-existant,
319 // 1 if the argument is not empty
321 if(tk == NULL || *tk == EOL)
325 *dst++ = (char)(questmark + '0');
329 if(tk != NULL) // arg# is in range, so expand it
331 // Reverse-translation from a token number to a string. This is a hack.
332 // It might be better table-driven.
334 if((*tk >= KW_D0) && !rdsp && !rgpu) {
335 d = regname[(int)*tk++ - KW_D0];
337 } else if((*tk >= KW_R0) && (*tk <= KW_R31)) {
338 d = riscregname[(int)*tk++ - KW_R0];
363 sprintf(numbuf, "$%lx", (LONG)*tk++);
423 *dst++ = (char)*(tk-1);
427 // If 'd' != NULL, copy string to destination
432 DEBUG printf("d='%s'\n", d);
449 return(fatal("line too long as a result of macro expansion"));
453 // --- Get Next Line of Text from a Macro ----------------------------------------------------------
459 unsigned source_addr;
461 imacro = cur_inobj->inobj.imacro;
462 strp = imacro->im_nextln;
464 if(strp == NULL) // End-of-macro
467 imacro->im_nextln = (LONG *)*strp;
468 mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
470 if(!strcmp(imacro->im_macro->sname, "mjump") && !mjump_align) {
471 // if we need to adjust the alignment of the jump source address to meet the rules of
472 // gpu main execution we need to skip the first nop of the macro. This is simpler than
473 // trying to insert nop's mid macro.
474 source_addr = (orgactive) ? orgaddr : sloc;
476 if(source_addr % 4) {
477 strp = imacro->im_nextln;
478 if(strp == NULL) return(NULL);
479 imacro->im_nextln = (LONG *)*strp;
480 mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
485 return(imacro->im_lnbuf);
489 // --- Get Next Line of Text from a Repeat Block ---------------------------------------------------
496 irept = cur_inobj->inobj.irept;
497 strp = irept->ir_nextln; // initial null
499 // Do repeat at end of .rept block's string list
501 DEBUG printf("back-to-top-of-repeat-block count=%d\n", irept->ir_count);
502 irept->ir_nextln = irept->ir_firstln; // copy first line
503 if(irept->ir_count-- == 0) {
504 DEBUG printf("end-repeat-block\n");
507 strp = irept->ir_nextln; //strp
510 strcpy(irbuf, (char*)(irept->ir_nextln + 1));
512 DEBUG printf("repeat line='%s'\n", irbuf);
513 irept->ir_nextln = (LONG *)*strp;
519 // --- Include a Source File used at the Root, and for ".include" Files ----------------------------
522 int include(int handle, char *fname) {
527 if(verb_flag) printf("[Including: %s]\n", fname); // Verbose mode
529 // Alloc and initialize include-descriptors
530 inobj = a_inobj(SRC_IFILE);
531 ifile = inobj->inobj.ifile;
533 ifile->ifhandle = handle; // Setup file handle
534 ifile->ifind = ifile->ifcnt = 0; // Setup buffer indices
535 ifile->ifoldlineno = curlineno; // Save old line number
536 ifile->ifoldfname = curfname; // Save old filename
537 ifile->ifno = cfileno; // Save old file number
538 cfileno = ++filecount; // Compute new file number
539 curfname = nstring(fname); // Set current filename (alloc storage)
540 curlineno = 0; // Start on line zero
542 // Add another file to the file-record
543 fr = (FILEREC *)amem((LONG)sizeof(FILEREC));
544 fr->frec_next = NULL;
545 fr->frec_name = curfname;
547 filerec = fr; // Add first filerec
549 last_fr->frec_next = fr; // Append to list of filerecs
556 // --- Initialize Tokenizer ------------------------------------------------------------------------
559 void init_token(void) {
561 char *htab = "0123456789abcdefABCDEF"; // Hex character table
563 lnsave = 0; // Don't save lines
564 curfname = ""; // No file, empty filename
565 filecount = (WORD)-1;
566 cfileno = (WORD)-1; // cfileno gets bumped to 0
578 // Initialize hex, "dot" and tolower tables
579 for(i = 0; i < 128; ++i) {
582 tolowertab[i] = (char)i;
584 for(i = 0; htab[i] != EOS; ++i)
585 hextab[htab[i]] = (char)((i < 16) ? i : i - 6);
586 for(i = 'A'; i <= 'Z'; ++i)
587 tolowertab[i] |= 0x20;
589 // These characters are legal immediately after a period
590 dotxtab['b'] = DOTB; // .b .B .s .S
594 dotxtab['w'] = DOTW; // .w .W
596 dotxtab['l'] = DOTL; // .l .L
598 dotxtab['I'] = DOTI; // .l .L
603 // --- Pop the Current Input Level -----------------------------------------------------------------
613 // Pop IFENT levels until we reach the conditional assembly context we were at when the
614 // input object was entered.
615 while(ifent != inobj->in_ifent)
618 tok = inobj->in_otok; // Restore tok and otok
619 etok = inobj->in_etok;
621 switch(inobj->in_type) {
622 case SRC_IFILE: // Pop and release an IFILE
624 printf("[Leaving: %s]\n", curfname);
625 ifile = inobj->inobj.ifile;
626 ifile->if_link = f_ifile;
628 close(ifile->ifhandle); // Close source file
629 curfname = ifile->ifoldfname; // Set current filename
630 curlineno = ifile->ifoldlineno; // Set current line#
631 DEBUG printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno);
632 cfileno = ifile->ifno; // Restore current file number
634 case SRC_IMACRO: // Pop and release an IMACRO
635 imacro = inobj->inobj.imacro;
636 imacro->im_link = f_imacro;
639 case SRC_IREPT: // Pop and release an IREPT
640 DEBUG printf("dealloc IREPT\n");
641 p = inobj->inobj.irept->ir_firstln;
649 cur_inobj = inobj->in_link;
650 inobj->in_link = f_inobj;
658 // --- Get line from file into buf, return NULL on EOF or ptr to the start of a null-term line -----
667 readamt = -1; // 0 if last read() yeilded 0 bytes
668 fl = cur_inobj->inobj.ifile;
671 // Scan for next end-of-line; handle stupid text formats by treating \r\n the same as \n.
672 // (lone '\r' at end of buffer means we have to check for '\n').
675 d = &fl->ifbuf[fl->ifind];
677 for(p = d; i < j; ++i, ++p) {
678 if(*p == '\r' || *p == '\n') {
682 break; // Look for '\n' to eat
683 } else if(p[1] == '\n') {
696 // Handle hanging lines by ignoring them (Input file is exhausted, no \r or \n on last line)
697 if(!readamt && fl->ifcnt) {
703 // Truncate and return absurdly long lines.
704 if(fl->ifcnt >= QUANTUM) {
705 fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0';
707 return(&fl->ifbuf[fl->ifind]);
710 // Relocate what's left of a line to the beginning of the buffer, and read some more of the
711 // file in; return NULL if the buffer's empty and on EOF.
713 p = &fl->ifbuf[fl->ifind];
714 d = &fl->ifbuf[fl->ifcnt & 1];
715 for(i = 0; i < fl->ifcnt; ++i)
717 fl->ifind = fl->ifcnt & 1;
720 if((readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM)) < 0)
723 if((fl->ifcnt += readamt) == 0)
729 // --- Tokenize a Line -----------------------------------------------------------------------------
733 char *ln = NULL; // Ptr to current position in line
734 char *p; // Random character ptr
735 TOKEN *tk; // Token-deposit ptr
736 int state = 0; // State for keyword detector
737 int j = 0; // Var for keyword detector
738 char c; // Random char
739 VALUE v; // Random value
740 char *nullspot = NULL; // Spot to clobber for SYMBOL terminatn
741 int stuffnull; // 1:terminate SYMBOL '\0' at *nullspot
746 if(cur_inobj == NULL) // Return EOF if input stack is empty
749 // Get another line of input from the current input source: a file, a macro, or a repeat-block
750 switch(cur_inobj->in_type) {
753 // o bump source line number;
754 // o tag the listing-line with a space;
755 // o kludge lines generated by Alcyon C.
757 if((ln = getln()) == NULL) {
758 fpop(); // Pop input level
759 goto retry; // Try for more lines
761 ++curlineno; // Bump line number
764 // AS68 compatibility, throw away all lines starting with back-quotes, tildes, or '*'
765 // On other lines, turn the first '*' into a semi-colon.
766 if(*ln == '`' || *ln == '~' || *ln == '*') *ln = ';';
767 else for(p = ln; *p != EOS; ++p) {
777 // o Handle end-of-macro;
778 // o tag the listing-line with an at (@) sign.
780 if((ln = getmln()) == NULL) {
781 exitmac(); // Exit macro (pop args, do fpop(), etc)
782 goto retry; // Try for more lines...
788 // o Handle end-of-repeat-block;
789 // o tag the listing-line with a pound (#) sign.
791 if((ln = getrln()) == NULL) {
799 // Save text of the line. We only do this during listings and within macro-type blocks,
800 // since it is expensive to unconditionally copy every line.
801 if(lnsave) strcpy(lnbuf, ln);
803 // General house-keeping
804 tok = tokeol; // Set "tok" to EOL in case of error
805 tk = etok; // Reset token ptr
806 stuffnull = 0; // Don't stuff nulls
807 ++totlines; // Bump total #lines assembled
809 // See if the entire line is a comment. This is a win if the programmer puts in lots of comments
810 if(*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln+1) == '/'))) goto goteol;
812 // Main tokenization loop;
813 // o skip whitespace;
814 // o handle end-of-line;
816 // o handle single-character tokens (operators, etc.);
817 // o handle multiple-character tokens (constants, strings, etc.).
819 // Skip whitespace, handle EOL
820 while((int)chrtab[*ln] & WHITE)
823 // Handle EOL, comment with ';'
824 if(*ln == EOS || *ln == ';'|| ((*ln == '/') && (*(ln+1) == '/')))
827 // Handle start of symbol. Symbols are null-terminated in place. The termination is
828 // always one symbol behind, since there may be no place for a null in the case that
829 // an operator immediately follows the name.
832 if(stuffnull) // Terminate old symbol
834 v = 0; // Assume no DOT attrib follows symbol
836 p = nullspot = ln++; // Nullspot -> start of this symbol
838 // Find end of symbol (and compute its length)
839 for(j = 1; (int)chrtab[*ln] & CTSYM; ++j)
842 // Handle "DOT" special forms (like ".b") that follow a normal symbol or keyword:
844 *ln++ = EOS; // Terminate symbol
845 stuffnull = 0; // And never try it again
847 // Character following the `.' must have a DOT attribute, and the chararacter after
848 // THAT one must not have a start-symbol attribute (to prevent symbols that look
849 // like, for example, "zingo.barf", which might be a good idea anyway....)
850 if((((int)chrtab[*ln] & DOT) == 0) || ((int)dotxtab[*ln] <= 0))
851 return(error("[bwsl] must follow `.' in symbol"));
852 v = (VALUE)dotxtab[*ln++];
853 if((int)chrtab[*ln] & CTSYM)
854 return(error("misuse of `.', not allowed in symbols"));
857 // If the symbol is small, check to see if it's really the name of a register.
859 for(state = 0; state >= 0;) {
860 j = (int)tolowertab[*p++];
862 if(kwcheck[j] != state) {
867 if(*p == EOS || p == ln) {
878 //make j = -1 if time, date etc with no preceeding ^^
879 //defined, referenced, streq, macdef, date and time
882 case 113: // referenced
891 if(j < 0 || state < 0) {
893 *tk++ = (TOKEN)nullspot;
899 if(v) // Record attribute token (if any)
902 if(stuffnull) // Arrange for string termination
907 // Handle identity tokens
913 // Handle multiple-character tokens
922 case '\'': // 'string'
923 case '\"': // "string"
928 for(p = ln; *ln != EOS && *ln != c1;) {
933 return(error("unterminated string"));
962 warn("bad backslash code in string");
970 return(error("unterminated string"));
973 case '$': // $, hex constant
974 if((int)chrtab[*ln] & HDIGIT) {
976 while((int)hextab[*ln] >= 0)
977 v = (v << 4) + (int)hextab[*ln++];
979 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
980 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
981 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
987 case '<': // < or << or <> or <=
1005 case ':': // : or ::
1011 case '=': // = or ==
1017 case '>': // > or >> or >=
1031 case '%': // % or binary constant
1032 if(*ln < '0' || *ln > '1') {
1037 while(*ln >= '0' && *ln <= '1')
1038 v = (v << 1) + *ln++ - '0';
1040 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1041 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1042 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1047 case '@': // @ or octal constant
1048 if(*ln < '0' || *ln > '7') {
1053 while(*ln >= '0' && *ln <= '7')
1054 v = (v << 3) + *ln++ - '0';
1056 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1057 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1058 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1063 case '^': // ^ or ^^ <operator-name>
1069 if(((int)chrtab[*++ln] & STSYM) == 0) {
1070 error("invalid symbol following ^^");
1075 while((int)chrtab[*ln] & CTSYM)
1078 for(state = 0; state >= 0;) {
1079 // Get char, convert to lowercase
1081 if(j >= 'A' && j <= 'Z')
1085 if(kwcheck[j] != state) {
1090 if(*p == EOS || p == ln) {
1097 if(j < 0 || state < 0) {
1098 error("unknown symbol following ^^");
1105 interror(2); // Bad MULTX entry in chrtab
1111 // Handle decimal constant
1114 while((int)chrtab[*ln] & DIGIT)
1115 v = (v * 10) + *ln++ - '0';
1117 if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1118 if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1119 if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1126 // Handle illegal character
1127 return(error("illegal character"));
1130 // Terminate line of tokens and return "success."
1134 tok = etok; // Set tok to beginning of line
1135 if(stuffnull) // Terminate last SYMBOL
1143 // -------------------------------------------------------------------------------------------------
1144 // .GOTO <label> goto directive
1146 // The label is searched for starting from the first line of the current, enclosing macro
1147 // definition. If no enclosing macro exists, an error is generated.
1149 // A label is of the form:
1151 // :<name><whitespace>
1153 // The colon must appear in column 1. The label is stripped prior to macro expansion, and is NOT
1154 // subject to macro expansion. The whitespace may also be EOL.
1155 // -------------------------------------------------------------------------------------------------
1158 //int d_goto(WORD siz) {
1160 char *sym; // Label to search for
1161 LONG *defln; // Macro definition strings
1162 char *s1; // Temps for string comparison
1164 IMACRO *imacro; // Macro invocation block
1166 // Setup for the search
1167 if(*tok != SYMBOL) return(error("missing label"));
1168 sym = (char *)tok[1];
1171 if(cur_inobj->in_type != SRC_IMACRO) return(error("goto not in macro"));
1172 imacro = cur_inobj->inobj.imacro;
1173 defln = (LONG *)imacro->im_macro->svalue;
1175 // Find the label, starting with the first line.
1176 for(; defln != NULL; defln = (LONG *)*defln)
1177 if(*(char *)(defln + 1) == ':') {
1178 // Compare names (sleazo string compare)
1180 s2 = (char *)(defln + 1) + 1;
1182 if(*s1 == EOS) break;
1188 // Found the label, set new macro next-line and return.
1189 if((*s2 == EOS) || ((int)chrtab[*s2] & WHITE)) {
1190 imacro->im_nextln = defln;
1195 return(error("goto label not found"));