2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // TOKEN.C - Token Handling
4 // Copyright (C) 199x Landon Dyer, 2011-2017 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
16 #define DECL_KW // Declare keyword arrays
17 #define DEF_KW // Declare keyword values
18 #include "kwtab.h" // Incl generated keyword tables & defs
21 int lnsave; // 1; strcpy() text of current line
22 int curlineno; // Current line number
23 int totlines; // Total # of lines
24 int mjump_align = 0; // mjump alignment flag
25 char lntag; // Line tag
26 char * curfname; // Current filename
27 char tolowertab[128]; // Uppercase ==> lowercase
28 int8_t hextab[128]; // Table of hex values
29 char dotxtab[128]; // Table for ".b", ".s", etc.
30 char irbuf[LNSIZ]; // Text for .rept block line
31 char lnbuf[LNSIZ]; // Text of current line
32 WORD filecount; // Unique file number counter
33 WORD cfileno; // Current file number
34 TOKEN * tok; // Ptr to current token
35 TOKEN * etok; // Ptr past last token in tokbuf[]
36 TOKEN tokeol[1] = {EOL}; // Bailout end-of-line token
37 char * string[TOKBUFSIZE*2]; // Token buffer string pointer storage
39 // File record, used to maintain a list of every include file ever visited
40 #define FILEREC struct _filerec
50 INOBJ * cur_inobj; // Ptr current input obj (IFILE/IMACRO)
51 static INOBJ * f_inobj; // Ptr list of free INOBJs
52 static IFILE * f_ifile; // Ptr list of free IFILEs
53 static IMACRO * f_imacro; // Ptr list of free IMACROs
55 static TOKEN tokbuf[TOKBUFSIZE]; // Token buffer (stack-like, all files)
58 ILLEG, ILLEG, ILLEG, ILLEG, // NUL SOH STX ETX
59 ILLEG, ILLEG, ILLEG, ILLEG, // EOT ENQ ACK BEL
60 ILLEG, WHITE, ILLEG, ILLEG, // BS HT LF VT
61 WHITE, ILLEG, ILLEG, ILLEG, // FF CR SO SI
63 ILLEG, ILLEG, ILLEG, ILLEG, // DLE DC1 DC2 DC3
64 ILLEG, ILLEG, ILLEG, ILLEG, // DC4 NAK SYN ETB
65 ILLEG, ILLEG, ILLEG, ILLEG, // CAN EM SUB ESC
66 ILLEG, ILLEG, ILLEG, ILLEG, // FS GS RS US
68 WHITE, MULTX, MULTX, SELF, // SP ! " #
69 MULTX+CTSYM, MULTX, SELF, MULTX, // $ % & '
70 SELF, SELF, SELF, SELF, // ( ) * +
71 SELF, SELF, STSYM, SELF, // , - . /
73 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 0 1
74 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 2 3
75 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 4 5
76 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 6 7
77 DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM, // 8 9
79 MULTX, MULTX, MULTX, STSYM+CTSYM, // < = > ?
81 MULTX, STSYM+CTSYM+HDIGIT, // @ A
82 (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // B C
83 STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // D E
84 STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // F G
85 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // H I J K
86 (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // L M N O
88 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // P Q R S
89 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // T U V W
90 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // X Y Z [
91 SELF, SELF, MULTX, STSYM+CTSYM, // \ ] ^ _
93 ILLEG, STSYM+CTSYM+HDIGIT, // ` a
94 (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // b c
95 STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT, // d e
96 STSYM+CTSYM+HDIGIT, STSYM+CTSYM, // f g
97 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // h i j k
98 (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // l m n o
100 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // p q r s
101 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM, // t u v w
102 STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF, // x y z {
103 SELF, SELF, SELF, ILLEG // | } ~ DEL
106 // Names of registers
107 static char * regname[] = {
108 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
109 "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
110 "pc", "ssp", "usp", "sr", "ccr"
113 static char * riscregname[] = {
114 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
115 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
116 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
117 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
122 // Initialize tokenizer
124 void InitTokenizer(void)
127 char * htab = "0123456789abcdefABCDEF"; // Hex character table
129 lnsave = 0; // Don't save lines
130 curfname = ""; // No file, empty filename
131 filecount = (WORD)-1;
132 cfileno = (WORD)-1; // cfileno gets bumped to 0
144 // Initialize hex, "dot" and tolower tables
149 tolowertab[i] = (char)i;
152 for(i=0; htab[i]!=EOS; i++)
153 hextab[htab[i]] = (char)((i < 16) ? i : i - 6);
155 for(i='A'; i<='Z'; i++)
156 tolowertab[i] |= 0x20;
158 // These characters are legal immediately after a period
159 dotxtab['b'] = DOTB; // .b .B .s .S
163 dotxtab['w'] = DOTW; // .w .W
165 dotxtab['l'] = DOTL; // .l .L
167 dotxtab['i'] = DOTI; // .i .I (???)
172 void SetFilenameForErrorReporting(void)
176 // Check for absolute top filename (this should never happen)
179 curfname = "(*top*)";
183 FILEREC * fr = filerec;
185 // Advance to the correct record...
186 while (fr != NULL && fnum != 0)
192 // Check for file # record not found (this should never happen either)
195 curfname = "(*NOT FOUND*)";
199 curfname = fr->frec_name;
204 // Allocate an IFILE or IMACRO
206 INOBJ * a_inobj(int typ)
212 // Allocate and initialize INOBJ first
214 inobj = malloc(sizeof(INOBJ));
218 f_inobj = f_inobj->in_link;
223 case SRC_IFILE: // Alloc and init an IFILE
225 ifile = malloc(sizeof(IFILE));
229 f_ifile = f_ifile->if_link;
232 inobj->inobj.ifile = ifile;
234 case SRC_IMACRO: // Alloc and init an IMACRO
235 if (f_imacro == NULL)
236 imacro = malloc(sizeof(IMACRO));
240 f_imacro = f_imacro->im_link;
243 inobj->inobj.imacro = imacro;
245 case SRC_IREPT: // Alloc and init an IREPT
246 inobj->inobj.irept = malloc(sizeof(IREPT));
247 DEBUG printf("alloc IREPT\n");
251 // Install INOBJ on top of input stack
252 inobj->in_ifent = ifent; // Record .if context on entry
253 inobj->in_type = (WORD)typ;
254 inobj->in_otok = tok;
255 inobj->in_etok = etok;
256 inobj->in_link = cur_inobj;
264 // Perform macro substitution from 'orig' to 'dest'. Return OK or some error.
265 // A macro reference is in one of two forms:
266 // \name <non-name-character>
268 // A doubled backslash (\\) is compressed to a single backslash (\).
269 // Argument definitions have been pre-tokenized, so we have to turn them back
270 // into text. This means that numbers, in particular, become hex, regardless of
271 // their representation when the macro was invoked. This is a hack.
272 // A label may appear at the beginning of the line:
273 // :<name><whitespace>
274 // (the colon must be in the first column). These labels are stripped before
275 // macro expansion takes place.
277 int ExpandMacro(char * src, char * dest, int destsiz)
280 int questmark; // \? for testing argument existence
281 char mname[128]; // Assume max size of a formal arg name
282 char numbuf[20]; // Buffer for text of CONSTs
285 char ** symbolString;
287 DEBUG { printf("ExM: src=\"%s\"\n", src); }
289 IMACRO * imacro = cur_inobj->inobj.imacro;
290 int macnum = (int)(imacro->im_macro->sattr);
292 char * dst = dest; // Next dest slot
293 char * edst = dest + destsiz - 1; // End + 1(?) of dest buffer
295 // Check for (and skip over) any "label" on the line
301 while (*s != EOS && !(chrtab[*s] & WHITE))
305 s++; // Skip first whitespace
308 // Expand the rest of the line
311 // Copy single character
317 // Skip comments in case a loose @ or \ is in there
318 // In that case the tokeniser was trying to expand it.
319 if ((*s == ';') || ((*s == '/') && (*(s + 1) == '/')))
324 // Do macro expansion
332 case '\\': // \\, \ (collapse to single backslash)
338 case '?': // \? <macro> set `questmark' flag
342 case '#': // \#, number of arguments
343 sprintf(numbuf, "%d", (int)imacro->im_nargs);
345 case '!': // \! size suffix supplied on invocation
346 switch ((int)imacro->im_siz)
348 case SIZN: d = ""; break;
349 case SIZB: d = ".b"; break;
350 case SIZW: d = ".w"; break;
351 case SIZL: d = ".l"; break;
355 case '~': // ==> unique label string Mnnnn...
356 sprintf(numbuf, "M%u", curuniq);
372 return error("missing argument name");
375 // \n ==> argument number 'n', 0..9
376 if (chrtab[*s] & DIGIT)
386 // Get argument name: \name, \{name}
396 while (chrtab[*s] & CTSYM);
401 for(++s; *s != EOS && *s != '}';)
405 return error("missing '}'");
412 // Lookup the argument and copy its (string) value into the
413 // destination string
414 DEBUG printf("argument='%s'\n", mname);
416 if ((arg = lookup(mname, MACARG, macnum)) == NULL)
417 return errors("undefined argument: '%s'", mname);
420 // Convert a string of tokens (terminated with EOL) back into
421 // text. If an argument is out of range (not specified in the
422 // macro invocation) then it is ignored.
423 i = (int)arg->svalue;
425 DEBUG printf("~argnumber=%d (argBase=%u)\n", i, imacro->argBase);
428 if (i < imacro->im_nargs)
433 tk = argPtrs[imacro->argBase + i];
435 tk = imacro->argument[i].token;
436 symbolString = imacro->argument[i].string;
439 // printf("ExM: Preparing to parse argument #%u...\n", i);
446 // 0 if the argument is empty or non-existant,
447 // 1 if the argument is not empty
450 if (tk == NULL || *tk == EOL)
456 *dst++ = (char)(questmark + '0');
460 // Argument # is in range, so expand it
465 // Reverse-translation from a token number to a string.
466 // This is a hack. It might be better table-driven.
469 if ((*tk >= KW_D0) && !rdsp && !rgpu)
471 d = regname[(int)*tk++ - KW_D0];
474 else if ((*tk >= KW_R0) && (*tk <= KW_R31))
476 d = riscregname[(int)*tk++ - KW_R0];
485 // d = (char *)*tk++;
488 // This fix should be done for strings too
489 d = symbolString[*tk++];
490 DEBUG printf("ExM: SYMBOL=\"%s\"", d);
495 // d = (char *)*tk++;
498 d = symbolString[*tk++];
519 // Shamus: Changing the format specifier from %lx to %ux caused
520 // the assembler to choke on legitimate code... Need to investigate
521 // this further before changing anything else here!
523 sprintf(numbuf, "$%lx", (LONG)*tk++);
587 *dst++ = (char)*(tk - 1);
592 // If 'd' != NULL, copy string to destination
596 DEBUG printf("d='%s'\n", d);
615 DEBUG { printf("ExM: dst=\"%s\"\n", dest); }
620 DEBUG printf("*** OVERFLOW LINE ***\n%s\n", dest);
621 return fatal("line too long as a result of macro expansion");
626 // Get next line of text from a macro
628 char * GetNextMacroLine(void)
630 IMACRO * imacro = cur_inobj->inobj.imacro;
631 // LONG * strp = imacro->im_nextln;
632 struct LineList * strp = imacro->im_nextln;
634 if (strp == NULL) // End-of-macro
637 imacro->im_nextln = strp->next;
638 // ExpandMacro((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
639 ExpandMacro(strp->line, imacro->im_lnbuf, LNSIZ);
641 return imacro->im_lnbuf;
646 // Get next line of text from a repeat block
648 char * GetNextRepeatLine(void)
651 IREPT * irept = cur_inobj->inobj.irept;
652 LONG * strp = irept->ir_nextln; // initial null
654 // Do repeat at end of .rept block's string list
657 DEBUG printf("back-to-top-of-repeat-block count=%d\n", (int)irept->ir_count);
658 irept->ir_nextln = irept->ir_firstln; // copy first line
660 if (irept->ir_count-- == 0)
662 DEBUG printf("end-repeat-block\n");
666 strp = irept->ir_nextln;
669 strcpy(irbuf, (char *)(irept->ir_nextln + 1));
670 DEBUG printf("repeat line='%s'\n", irbuf);
671 irept->ir_nextln = (LONG *)*strp;
678 // Include a source file used at the root, and for ".include" files
680 int include(int handle, char * fname)
684 printf("[include: %s, cfileno=%u]\n", fname, cfileno);
686 // Alloc and initialize include-descriptors
687 INOBJ * inobj = a_inobj(SRC_IFILE);
688 IFILE * ifile = inobj->inobj.ifile;
690 ifile->ifhandle = handle; // Setup file handle
691 ifile->ifind = ifile->ifcnt = 0; // Setup buffer indices
692 ifile->ifoldlineno = curlineno; // Save old line number
693 ifile->ifoldfname = curfname; // Save old filename
694 ifile->ifno = cfileno; // Save old file number
696 // NB: This *must* be preincrement, we're adding one to the filecount here!
697 cfileno = ++filecount; // Compute NEW file number
698 curfname = strdup(fname); // Set current filename (alloc storage)
699 curlineno = 0; // Start on line zero
701 // Add another file to the file-record
702 FILEREC * fr = (FILEREC *)malloc(sizeof(FILEREC));
703 fr->frec_next = NULL;
704 fr->frec_name = curfname;
707 filerec = fr; // Add first filerec
709 last_fr->frec_next = fr; // Append to list of filerecs
712 DEBUG printf("[include: curfname: %s, cfileno=%u]\n", curfname, cfileno);
719 // Pop the current input level
726 INOBJ * inobj = cur_inobj;
730 // Pop IFENT levels until we reach the conditional assembly context we
731 // were at when the input object was entered.
732 int numUnmatched = 0;
734 while (ifent != inobj->in_ifent)
736 if (d_endif() != 0) // Something bad happened during endif parsing?
737 return -1; // If yes, bail instead of getting stuck in a loop
742 // Give a warning to the user that we had to wipe their bum for them
743 if (numUnmatched > 0)
744 warni("missing %d .endif(s)", numUnmatched);
746 tok = inobj->in_otok; // Restore tok and otok
747 etok = inobj->in_etok;
749 switch (inobj->in_type)
751 case SRC_IFILE: // Pop and release an IFILE
753 printf("[Leaving: %s]\n", curfname);
755 ifile = inobj->inobj.ifile;
756 ifile->if_link = f_ifile;
758 close(ifile->ifhandle); // Close source file
759 if (debug) printf("[fpop (pre): curfname=%s]\n", curfname);
760 curfname = ifile->ifoldfname; // Set current filename
761 if (debug) printf("[fpop (post): curfname=%s]\n", curfname);
762 if (debug) printf("[fpop: (pre) cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno);
763 curlineno = ifile->ifoldlineno; // Set current line#
764 DEBUG printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno);
765 cfileno = ifile->ifno; // Restore current file number
766 if (debug) printf("[fpop: (post) cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno);
768 case SRC_IMACRO: // Pop and release an IMACRO
769 imacro = inobj->inobj.imacro;
770 imacro->im_link = f_imacro;
773 case SRC_IREPT: // Pop and release an IREPT
774 DEBUG printf("dealloc IREPT\n");
775 p = inobj->inobj.irept->ir_firstln;
786 cur_inobj = inobj->in_link;
787 inobj->in_link = f_inobj;
796 // Get line from file into buf, return NULL on EOF or ptr to the start of a
799 char * GetNextLine(void)
803 int readamt = -1; // 0 if last read() yeilded 0 bytes
804 IFILE * fl = cur_inobj->inobj.ifile;
808 // Scan for next end-of-line; handle stupid text formats by treating
809 // \r\n the same as \n. (lone '\r' at end of buffer means we have to
811 d = &fl->ifbuf[fl->ifind];
813 for(p=d, i=0, j=fl->ifcnt; i<j; i++, p++)
815 if (*p == '\r' || *p == '\n')
822 break; // Need to read more, then look for '\n' to eat
823 else if (p[1] == '\n')
827 // Cover up the newline with end-of-string sentinel
836 // Handle hanging lines by ignoring them (Input file is exhausted, no
837 // \r or \n on last line)
838 // Shamus: This is retarded. Never ignore any input!
839 if (!readamt && fl->ifcnt)
846 // Really should check to see if we're at the end of the buffer!
848 fl->ifbuf[fl->ifind + fl->ifcnt] = '\0';
850 return &fl->ifbuf[fl->ifind];
854 // Truncate and return absurdly long lines.
855 if (fl->ifcnt >= QUANTUM)
857 fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0';
859 return &fl->ifbuf[fl->ifind];
862 // Relocate what's left of a line to the beginning of the buffer, and
863 // read some more of the file in; return NULL if the buffer's empty and
867 p = &fl->ifbuf[fl->ifind];
868 d = &fl->ifbuf[fl->ifcnt & 1];
870 for(i=0; i<fl->ifcnt; i++)
873 fl->ifind = fl->ifcnt & 1;
876 readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM);
881 if ((fl->ifcnt += readamt) == 0)
890 int TokenizeLine(void)
892 char * ln = NULL; // Ptr to current position in line
893 char * p; // Random character ptr
894 TOKEN * tk; // Token-deposit ptr
895 int state = 0; // State for keyword detector
896 int j = 0; // Var for keyword detector
897 char c; // Random char
898 VALUE v; // Random value
899 char * nullspot = NULL; // Spot to clobber for SYMBOL termination
900 int stuffnull; // 1:terminate SYMBOL '\0' at *nullspot
902 int stringNum = 0; // Pointer to string locations in tokenized line
906 if (cur_inobj == NULL) // Return EOF if input stack is empty
909 // Get another line of input from the current input source: a file, a
910 // macro, or a repeat-block
911 switch (cur_inobj->in_type)
915 // o bump source line number;
916 // o tag the listing-line with a space;
917 // o kludge lines generated by Alcyon C.
919 if ((ln = GetNextLine()) == NULL)
921 if (debug) printf("TokenizeLine: Calling fpop() from SRC_IFILE...\n");
922 if (fpop() == 0) // Pop input level
923 goto retry; // Try for more lines
926 ifent->if_prev = (IFENT *) - 1; //Signal Assemble() that we have reached EOF with unbalanced if/endifs
931 curlineno++; // Bump line number
936 // AS68 compatibility, throw away all lines starting with
937 // back-quotes, tildes, or '*'
938 // On other lines, turn the first '*' into a semi-colon.
939 if (*ln == '`' || *ln == '~' || *ln == '*')
943 for(p=ln; *p!=EOS; p++)
956 // o Handle end-of-macro;
957 // o tag the listing-line with an at (@) sign.
959 if ((ln = GetNextMacroLine()) == NULL)
961 if (ExitMacro() == 0) // Exit macro (pop args, do fpop(), etc)
962 goto retry; // Try for more lines...
964 return TKEOF; // Oops, we got a non zero return code, signal EOF
970 // o Handle end-of-repeat-block;
971 // o tag the listing-line with a pound (#) sign.
973 if ((ln = GetNextRepeatLine()) == NULL)
975 if (debug) printf("TokenizeLine: Calling fpop() from SRC_IREPT...\n");
984 // Save text of the line. We only do this during listings and within
985 // macro-type blocks, since it is expensive to unconditionally copy every
990 // General house-keeping
991 tok = tokeol; // Set "tok" to EOL in case of error
992 tk = etok; // Reset token ptr
993 stuffnull = 0; // Don't stuff nulls
994 totlines++; // Bump total #lines assembled
996 // See if the entire line is a comment. This is a win if the programmer
997 // puts in lots of comments
998 if (*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln + 1) == '/')))
1001 // Main tokenization loop;
1002 // o skip whitespace;
1003 // o handle end-of-line;
1004 // o handle symbols;
1005 // o handle single-character tokens (operators, etc.);
1006 // o handle multiple-character tokens (constants, strings, etc.).
1009 // Skip whitespace, handle EOL
1010 while ((int)chrtab[*ln] & WHITE)
1013 // Handle EOL, comment with ';'
1014 if (*ln == EOS || *ln == ';'|| ((*ln == '/') && (*(ln + 1) == '/')))
1017 // Handle start of symbol. Symbols are null-terminated in place. The
1018 // termination is always one symbol behind, since there may be no place
1019 // for a null in the case that an operator immediately follows the name.
1024 if (stuffnull) // Terminate old symbol from previous pass
1027 v = 0; // Assume no DOT attrib follows symbol
1030 // In some cases, we need to check for a DOTx at the *beginning*
1031 // of a symbol, as the "start" of the line we're currently looking
1032 // at could be somewhere in the middle of that line!
1035 // Make sure that it's *only* a .[bwsl] following, and not the
1036 // start of a local symbol:
1037 if ((chrtab[*(ln + 1)] & DOT)
1038 && (dotxtab[*(ln + 1)] != 0)
1039 && !(chrtab[*(ln + 2)] & CTSYM))
1041 // We found a legitimate DOTx construct, so add it to the
1045 *tk++ = (TOKEN)dotxtab[*ln++];
1050 p = nullspot = ln++; // Nullspot -> start of this symbol
1052 // Find end of symbol (and compute its length)
1053 for(j=1; (int)chrtab[*ln]&CTSYM; j++)
1056 // Handle "DOT" special forms (like ".b") that follow a normal
1057 // symbol or keyword:
1060 *ln++ = EOS; // Terminate symbol
1061 stuffnull = 0; // And never try it again
1063 // Character following the `.' must have a DOT attribute, and
1064 // the chararacter after THAT one must not have a start-symbol
1065 // attribute (to prevent symbols that look like, for example,
1066 // "zingo.barf", which might be a good idea anyway....)
1067 if (((chrtab[*ln] & DOT) == 0) || (dotxtab[*ln] == 0))
1068 return error("[bwsl] must follow '.' in symbol");
1070 v = (VALUE)dotxtab[*ln++];
1072 if (chrtab[*ln] & CTSYM)
1073 return error("misuse of '.', not allowed in symbols");
1076 // If the symbol is small, check to see if it's really the name of
1080 for(state=0; state>=0;)
1082 j = (int)tolowertab[*p++];
1085 if (kwcheck[j] != state)
1091 if (*p == EOS || p == ln)
1105 // Make j = -1 if user tries to use a RISC register while in 68K mode
1106 if (!(rgpu || rdsp) && ((TOKEN)j >= KW_R0 && (TOKEN)j <= KW_R31))
1111 // Make j = -1 if time, date etc with no preceeding ^^
1112 // defined, referenced, streq, macdef, date and time
1115 case 112: // defined
1116 case 113: // referenced
1124 // If not tokenized keyword OR token was not found
1125 if ((j < 0) || (state < 0))
1129 //problem here: nullspot is a char * but TOKEN is a uint32_t. On a 64-bit
1130 //system, this will cause all kinds of mischief.
1132 *tk++ = (TOKEN)nullspot;
1134 string[stringNum] = nullspot;
1145 if (v) // Record attribute token (if any)
1148 if (stuffnull) // Arrange for string termination on next pass
1154 // Handle identity tokens
1161 // Handle multiple-character tokens
1167 case '!': // ! or !=
1177 case '\'': // 'string'
1180 stringtype = A8INT; // hardcoded for now, maybe this will change in the future
1183 case '\"': // "string"
1187 // More char * stuffing (8 bytes) into the space of 4 (TOKEN).
1188 // Need to figure out how to fix this crap.
1192 string[stringNum] = ln;
1197 for(p=ln; *ln!=EOS && *ln!=c1;)
1206 return(error("unterminated string"));
1235 warn("bad backslash code in string");
1245 return error("unterminated string");
1249 case '$': // $, hex constant
1250 if (chrtab[*ln] & HDIGIT)
1254 // Parse the hex value
1255 while (hextab[*ln] >= 0)
1256 v = (v << 4) + (int)hextab[*ln++];
1258 // ggn: Okay, some comments here are in order I think....
1259 // The original madmac sources didn't parse the size at
1260 // this point (i.e. .b/.w/.l). It was probably done at
1261 // another point, although it's unclear to me exactly
1262 // where. So why change this? My understanding (at least
1263 // from what SCPCD said on IRC) is that .w addressing
1264 // formats produce wrong code on jaguar (or doesn't execute
1265 // properly? something like that). So the code was changed
1266 // to mask off the upper bits depending on length (note: I
1267 // don't think .b is valid at all! I only know of .w/.l, so
1268 // this should probably be wiped). Then the code that
1269 // parses the constant and checks to see if it's between
1270 // $ffff0000 and $8000 never got triggered, so yay job
1271 // done! ...now say we want to assemble a st .prg. One of
1272 // the most widely spread optimisations is move.X expr.w,Y
1273 // (or vice versa, or both, anyway...) to access hardware
1274 // registers (which are mapped to $fxxxxx). This botchy
1275 // thing would create "hilarious" code while trying to
1276 // access hardware registers. So I made a condition to see
1277 // if st mode or jaguar is active and apply the both or
1278 // not. One last note: this is hardcoded to get optimised
1279 // for now on ST mode, i.e. it can't generate code like
1280 // move.w $00001234,d0 - it'll always get optimised to
1281 // move.w $1234.w,d0. It's probably ok, but maybe a warning
1282 // should be emitted? Or maybe finding a way to make it not
1283 // auto-optimise? I think it's ok for now...
1286 if (obj_format == BSD)
1288 if ((*(ln + 1) & 0xDF) == 'B')
1293 else if ((*(ln + 1) & 0xDF) == 'W')
1298 else if ((*(ln + 1) & 0xDF) == 'L')
1308 if (obj_format == ALCYON)
1310 if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1315 else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1326 case '<': // < or << or <> or <=
1345 case ':': // : or ::
1355 case '=': // = or ==
1365 case '>': // > or >> or >=
1380 case '%': // % or binary constant
1381 if (*ln < '0' || *ln > '1')
1389 while (*ln >= '0' && *ln <= '1')
1390 v = (v << 1) + *ln++ - '0';
1394 if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1400 if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1406 if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1415 case '@': // @ or octal constant
1416 if (*ln < '0' || *ln > '7')
1424 while (*ln >= '0' && *ln <= '7')
1425 v = (v << 3) + *ln++ - '0';
1429 if ((*(ln+1) == 'b') || (*(ln+1) == 'B'))
1435 if ((*(ln+1) == 'w') || (*(ln+1) == 'W'))
1441 if ((*(ln+1) == 'l') || (*(ln+1) == 'L'))
1450 case '^': // ^ or ^^ <operator-name>
1457 if (((int)chrtab[*++ln] & STSYM) == 0)
1459 error("invalid symbol following ^^");
1465 while ((int)chrtab[*ln] & CTSYM)
1468 for(state=0; state>=0;)
1470 // Get char, convert to lowercase
1473 if (j >= 'A' && j <= 'Z')
1478 if (kwcheck[j] != state)
1484 if (*p == EOS || p == ln)
1493 if (j < 0 || state < 0)
1495 error("unknown symbol following ^^");
1502 interror(2); // Bad MULTX entry in chrtab
1507 // Handle decimal constant
1512 while ((int)chrtab[*ln] & DIGIT)
1513 v = (v * 10) + *ln++ - '0';
1515 // See if there's a .[bwl] after the constant & deal with it if so
1518 if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1523 else if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1528 else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1536 //printf("CONST: %i\n", v);
1540 // Handle illegal character
1541 return error("illegal character");
1544 // Terminate line of tokens and return "success."
1547 tok = etok; // Set tok to beginning of line
1549 if (stuffnull) // Terminate last SYMBOL
1559 // .GOTO <label> goto directive
1561 // The label is searched for starting from the first line of the current,
1562 // enclosing macro definition. If no enclosing macro exists, an error is
1565 // A label is of the form:
1567 // :<name><whitespace>
1569 // The colon must appear in column 1. The label is stripped prior to macro
1570 // expansion, and is NOT subject to macro expansion. The whitespace may also
1573 //int d_goto(WORD siz) {
1575 int d_goto(WORD unused)
1579 // Setup for the search
1581 return error("missing label");
1583 // sym = (char *)tok[1];
1584 char * sym = string[tok[1]];
1587 if (cur_inobj->in_type != SRC_IMACRO)
1588 return error("goto not in macro");
1590 IMACRO * imacro = cur_inobj->inobj.imacro;
1591 // defln = (LONG *)imacro->im_macro->svalue;
1592 struct LineList * defln = imacro->im_macro->lineList;
1594 // Find the label, starting with the first line.
1595 for(; defln!=NULL; defln=defln->next)
1597 // if (*(char *)(defln + 1) == ':')
1598 if (defln->line[0] == ':')
1600 // Compare names (sleazo string compare)
1601 // This string compare is not right. Doesn't check for lengths.
1602 // (actually it does, but in a crappy, unclear way.)
1603 WARNING(!!!! Bad string comparison !!!)
1605 // s2 = (char *)(defln + 1) + 1;
1619 // Found the label, set new macro next-line and return.
1620 if ((*s2 == EOS) || ((int)chrtab[*s2] & WHITE))
1622 imacro->im_nextln = defln;
1628 return error("goto label not found");
1632 void DumpTokenBuffer(void)
1635 printf("Tokens [%X]: ", sloc);
1637 for(t=tokbuf; *t!=EOL; t++)
1641 else if (*t == CONST)
1644 printf("[CONST: $%X]", (uint32_t)*t);
1646 else if (*t == ACONST)
1648 else if (*t == STRING)
1651 printf("[STRING:\"%s\"]", string[*t]);
1653 else if (*t == SYMBOL)
1656 printf("[SYMBOL:\"%s\"]", string[*t]);
1660 else if (*t == TKEOF)
1662 else if (*t == DEQUALS)
1663 printf("[DEQUALS]");
1668 else if (*t == DCOLON)
1680 else if (*t == UNMINUS)
1681 printf("[UNMINUS]");
1682 else if (*t == DOTB)
1684 else if (*t == DOTW)
1686 else if (*t == DOTL)
1688 else if (*t == DOTI)
1690 else if (*t == ENDEXPR)
1691 printf("[ENDEXPR]");
1692 else if (*t == CR_ABSCOUNT)
1693 printf("[CR_ABSCOUNT]");
1694 else if (*t == CR_DEFINED)
1695 printf("[CR_DEFINED]");
1696 else if (*t == CR_REFERENCED)
1697 printf("[CR_REFERENCED]");
1698 else if (*t == CR_STREQ)
1699 printf("[CR_STREQ]");
1700 else if (*t == CR_MACDEF)
1701 printf("[CR_MACDEF]");
1702 else if (*t == CR_TIME)
1703 printf("[CR_TIME]");
1704 else if (*t == CR_DATE)
1705 printf("[CR_DATE]");
1706 else if (*t >= 0x20 && *t <= 0x2F)
1707 printf("[%c]", (char)*t);
1708 else if (*t >= 0x3A && *t <= 0x3F)
1709 printf("[%c]", (char)*t);
1710 else if (*t >= 0x80 && *t <= 0x87)
1711 printf("[D%u]", ((uint32_t)*t) - 0x80);
1712 else if (*t >= 0x88 && *t <= 0x8F)
1713 printf("[A%u]", ((uint32_t)*t) - 0x88);
1715 printf("[%X:%c]", (uint32_t)*t, (char)*t);