]> Shamusworld >> Repos - rmac/blob - token.c
6f42cca5d8f5306904c387e23f4f615bd928bfae
[rmac] / token.c
1 //
2 // RMAC - Reboot's Macro Assembler for all Atari computers
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
7 //
8
9 #include "token.h"
10 #include "direct.h"
11 #include "error.h"
12 #include "macro.h"
13 #include "procln.h"
14 #include "sect.h"
15 #include "symbol.h"
16
17 #define DECL_KW                         // Declare keyword arrays
18 #define DEF_KW                          // Declare keyword values
19 #include "kwtab.h"                      // Incl generated keyword tables & defs
20
21
22 int lnsave;                                     // 1; strcpy() text of current line
23 uint16_t curlineno;                     // Current line number (64K max currently)
24 int totlines;                           // Total # of lines
25 int mjump_align = 0;            // mjump alignment flag
26 char lntag;                                     // Line tag
27 char * curfname;                        // Current filename
28 char tolowertab[128];           // Uppercase ==> lowercase
29 int8_t hextab[128];                     // Table of hex values
30 char dotxtab[128];                      // Table for ".b", ".s", etc.
31 char irbuf[LNSIZ];                      // Text for .rept block line
32 char lnbuf[LNSIZ];                      // Text of current line
33 WORD filecount;                         // Unique file number counter
34 WORD cfileno;                           // Current file number
35 TOKEN * tok;                            // Ptr to current token
36 TOKEN * etok;                           // Ptr past last token in tokbuf[]
37 TOKEN tokeol[1] = {EOL};        // Bailout end-of-line token
38 char * string[TOKBUFSIZE*2];    // Token buffer string pointer storage
39
40 // File record, used to maintain a list of every include file ever visited
41 #define FILEREC struct _filerec
42 FILEREC
43 {
44    FILEREC * frec_next;
45    char * frec_name;
46 };
47
48 FILEREC * filerec;
49 FILEREC * last_fr;
50
51 INOBJ * cur_inobj;                                              // Ptr current input obj (IFILE/IMACRO)
52 static INOBJ * f_inobj;                                 // Ptr list of free INOBJs
53 static IFILE * f_ifile;                                 // Ptr list of free IFILEs
54 static IMACRO * f_imacro;                               // Ptr list of free IMACROs
55
56 static TOKEN tokbuf[TOKBUFSIZE];                // Token buffer (stack-like, all files)
57
58 uint8_t chrtab[0x100] = {
59         ILLEG, ILLEG, ILLEG, ILLEG,                     // NUL SOH STX ETX
60         ILLEG, ILLEG, ILLEG, ILLEG,                     // EOT ENQ ACK BEL
61         ILLEG, WHITE, ILLEG, ILLEG,                     // BS HT LF VT
62         WHITE, ILLEG, ILLEG, ILLEG,                     // FF CR SO SI
63
64         ILLEG, ILLEG, ILLEG, ILLEG,                     // DLE DC1 DC2 DC3
65         ILLEG, ILLEG, ILLEG, ILLEG,                     // DC4 NAK SYN ETB
66         ILLEG, ILLEG, ILLEG, ILLEG,                     // CAN EM SUB ESC
67         ILLEG, ILLEG, ILLEG, ILLEG,                     // FS GS RS US
68
69         WHITE, MULTX, MULTX, SELF,                      // SP ! " #
70         MULTX+CTSYM, MULTX, SELF, MULTX,        // $ % & '
71         SELF, SELF, SELF, SELF,                         // ( ) * +
72         SELF, SELF, STSYM, SELF,                        // , - . /
73
74         DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,         // 0 1
75         DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,         // 2 3
76         DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,         // 4 5
77         DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,         // 6 7
78         DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,         // 8 9
79         MULTX, MULTX,                                                           // : ;
80         MULTX, MULTX, MULTX, STSYM+CTSYM,                       // < = > ?
81
82         MULTX, STSYM+CTSYM+HDIGIT,                                      // @ A
83         DOT+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,     // B C
84         DOT+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,     // D E
85         STSYM+CTSYM+HDIGIT, STSYM+CTSYM,                        // F G
86         STSYM+CTSYM, DOT+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // H I J K
87         DOT+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // L M N O
88
89         DOT+STSYM+CTSYM, DOT+STSYM+CTSYM, STSYM+CTSYM, DOT+STSYM+CTSYM, // P Q R S
90         STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, DOT+STSYM+CTSYM, // T U V W
91         STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF,// X Y Z [
92         SELF, SELF, MULTX, STSYM+CTSYM,                         // \ ] ^ _
93
94         ILLEG, STSYM+CTSYM+HDIGIT,                                      // ` a
95         DOT+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,     // b c
96         DOT+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,     // d e
97         STSYM+CTSYM+HDIGIT, STSYM+CTSYM,                        // f g
98         STSYM+CTSYM, DOT+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // h i j k
99         DOT+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, // l m n o
100
101         DOT+STSYM+CTSYM, DOT+STSYM+CTSYM, STSYM+CTSYM, DOT+STSYM+CTSYM, // p q r s
102         STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, DOT+STSYM+CTSYM, // t u v w
103         DOT+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF,                // x y z {
104         SELF, SELF, SELF, ILLEG,                                        // | } ~ DEL
105
106         // Anything above $7F is illegal (and yes, we need to check for this,
107         // otherwise you get strange and spurious errors that will lead you astray)
108         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
109         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
110         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
111         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
112         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
113         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
114         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
115         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
116         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
117         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
118         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
119         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
120         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
121         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
122         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG,
123         ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG, ILLEG
124 };
125
126 // Names of registers
127 static char * regname[] = {
128         "d0","d1","d2","d3","d4","d5","d6","d7", // 128,135
129         "a0","a1","a2","a3","a4","a5","a6","sp", // 136,143
130         "ssp","pc","sr","ccr","regequ","set","reg","r0", // 144,151
131         "r1","r2","r3","r4","r5","r6","r7","r8", // 152,159
132         "r9","r10","r11","r12","r13","r14","r15","r16", // 160,167
133         "r17","r18","r19","r20","r21","r22","r23","r24", // 168,175
134         "r25","r26","r27","r28","r29","r30","r31","ccdef", // 176,183
135         "usp","ic40","dc40","bc40","sfc","dfc","","vbr", // 184,191
136         "cacr","caar","msp","isp","tc","itt0","itt1","dtt0", // 192,199
137         "dtt1","mmusr","urp","srp","iacr0","iacr1","dacr0","dacr1", // 200,207
138         "tt0","tt1","crp","","","","","", // 208,215
139         "","","","","fpiar","fpsr","fpcr","", // 216,223
140         "fp0","fp1","fp2","fp3","fp4","fp5","fp6","fp7", // 224,231
141         "","","","","","","","", // 232,239
142         "","","","","","","","", // 240,247
143         "","","","","","","","", // 248,255
144         "","","","","x0","x1","y0","y1", // 256,263
145         "","b0","","b2","","b1","a","b", // 264,271
146         "mr","omr","la","lc","ssh","ssl","ss","", // 272,279
147         "n0","n1","n2","n3","n4","n5","n6","n7", // 280,287
148         "m0","m1","m2","m3","m4","m5","m6","m7", // 288,295
149         "","","","","","","l","p", // 296,303
150         "mr","omr","la","lc","ssh","ssl","ss","", // 304,311
151         "a10","b10","x","y","","","ab","ba"  // 312,319
152 };
153
154 static char * riscregname[] = {
155          "r0",  "r1",  "r2",  "r3",  "r4", "r5",   "r6",  "r7",
156          "r8",  "r9", "r10", "r11", "r12", "r13", "r14", "r15",
157         "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
158         "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
159 };
160
161
162 //
163 // Initialize tokenizer
164 //
165 void InitTokenizer(void)
166 {
167         int i;                                                                  // Iterator
168         char * htab = "0123456789abcdefABCDEF"; // Hex character table
169
170         lnsave = 0;                                                             // Don't save lines
171         curfname = "";                                                  // No file, empty filename
172         filecount = (WORD)-1;
173         cfileno = (WORD)-1;                                             // cfileno gets bumped to 0
174         curlineno = 0;
175         totlines = 0;
176         etok = tokbuf;
177         f_inobj = NULL;
178         f_ifile = NULL;
179         f_imacro = NULL;
180         cur_inobj = NULL;
181         filerec = NULL;
182         last_fr = NULL;
183         lntag = SPACE;
184
185         // Initialize hex, "dot" and tolower tables
186         for(i=0; i<128; i++)
187         {
188                 hextab[i] = -1;
189                 dotxtab[i] = 0;
190                 tolowertab[i] = (char)i;
191         }
192
193         for(i=0; htab[i]!=EOS; i++)
194                 hextab[htab[i]] = (char)((i < 16) ? i : i - 6);
195
196         for(i='A'; i<='Z'; i++)
197                 tolowertab[i] |= 0x20;
198
199         // These characters are legal immediately after a period
200         dotxtab['b'] = DOTB;                                    // .b .B .s .S
201         dotxtab['B'] = DOTB;
202         //dotxtab['s'] = DOTB;
203         //dotxtab['S'] = DOTB;
204         dotxtab['w'] = DOTW;                                    // .w .W
205         dotxtab['W'] = DOTW;
206         dotxtab['l'] = DOTL;                                    // .l .L
207         dotxtab['L'] = DOTL;
208         dotxtab['i'] = DOTI;                                    // .i .I (???)
209         dotxtab['I'] = DOTI;
210         dotxtab['D'] = DOTD;                                    // .d .D (quad word)
211         dotxtab['d'] = DOTD;
212         dotxtab['S'] = DOTS;                                    // .s .S
213         dotxtab['s'] = DOTS;
214         dotxtab['Q'] = DOTQ;                                    // .q .Q
215         dotxtab['q'] = DOTQ;
216         dotxtab['X'] = DOTX;                                    // .x .x
217         dotxtab['x'] = DOTX;
218         dotxtab['P'] = DOTP;                                    // .p .P
219         dotxtab['p'] = DOTP;
220 }
221
222
223 void SetFilenameForErrorReporting(void)
224 {
225         WORD fnum = cfileno;
226
227         // Check for absolute top filename (this should never happen)
228         if (fnum == -1)
229         {
230                 curfname = "(*top*)";
231                 return;
232         }
233
234         FILEREC * fr = filerec;
235
236         // Advance to the correct record...
237         while (fr != NULL && fnum != 0)
238         {
239                 fr = fr->frec_next;
240                 fnum--;
241         }
242
243         // Check for file # record not found (this should never happen either)
244         if (fr == NULL)
245         {
246                 curfname = "(*NOT FOUND*)";
247                 return;
248         }
249
250         curfname = fr->frec_name;
251 }
252
253
254 //
255 // Allocate an IFILE or IMACRO
256 //
257 INOBJ * a_inobj(int typ)
258 {
259         INOBJ * inobj;
260         IFILE * ifile;
261         IMACRO * imacro;
262
263         // Allocate and initialize INOBJ first
264         if (f_inobj == NULL)
265                 inobj = malloc(sizeof(INOBJ));
266         else
267         {
268                 inobj = f_inobj;
269                 f_inobj = f_inobj->in_link;
270         }
271
272         switch (typ)
273         {
274         case SRC_IFILE:                                                 // Alloc and init an IFILE
275                 if (f_ifile == NULL)
276                         ifile = malloc(sizeof(IFILE));
277                 else
278                 {
279                         ifile = f_ifile;
280                         f_ifile = f_ifile->if_link;
281                 }
282
283                 inobj->inobj.ifile = ifile;
284                 break;
285         case SRC_IMACRO:                                                // Alloc and init an IMACRO
286                 if (f_imacro == NULL)
287                         imacro = malloc(sizeof(IMACRO));
288                 else
289                 {
290                         imacro = f_imacro;
291                         f_imacro = f_imacro->im_link;
292                 }
293
294                 inobj->inobj.imacro = imacro;
295                 break;
296         case SRC_IREPT:                                                 // Alloc and init an IREPT
297                 inobj->inobj.irept = malloc(sizeof(IREPT));
298                 DEBUG { printf("alloc IREPT\n"); }
299                 break;
300         }
301
302         // Install INOBJ on top of input stack
303         inobj->in_ifent = ifent;                                // Record .if context on entry
304         inobj->in_type = (WORD)typ;
305         inobj->in_otok = tok;
306         inobj->in_etok = etok;
307         inobj->in_link = cur_inobj;
308         cur_inobj = inobj;
309
310         return inobj;
311 }
312
313
314 //
315 // Perform macro substitution from 'orig' to 'dest'. Return OK or some error.
316 // A macro reference is in one of two forms:
317 // \name <non-name-character>
318 // \{name}
319 // A doubled backslash (\\) is compressed to a single backslash (\).
320 // Argument definitions have been pre-tokenized, so we have to turn them back
321 // into text. This means that numbers, in particular, become hex, regardless of
322 // their representation when the macro was invoked. This is a hack.
323 // A label may appear at the beginning of the line:
324 // :<name><whitespace>
325 // (the colon must be in the first column). These labels are stripped before
326 // macro expansion takes place.
327 //
328 int ExpandMacro(char * src, char * dest, int destsiz)
329 {
330         int i;
331         int questmark;                  // \? for testing argument existence
332         char mname[128];                // Assume max size of a formal arg name
333         char numbuf[20];                // Buffer for text of CONSTs
334         TOKEN * tk;
335         SYM * arg;
336         char ** symbolString;
337
338         DEBUG { printf("ExM: src=\"%s\"\n", src); }
339
340         IMACRO * imacro = cur_inobj->inobj.imacro;
341         int macnum = (int)(imacro->im_macro->sattr);
342
343         char * dst = dest;                                              // Next dest slot
344         char * edst = dest + destsiz - 1;               // End + 1(?) of dest buffer
345
346         // Check for (and skip over) any "label" on the line
347         char * s = src;
348         char * d = NULL;
349
350         if (*s == ':')
351         {
352                 while (*s != EOS && !(chrtab[*s] & WHITE))
353                         s++;
354
355                 if (*s != EOS)
356                         s++;                                                    // Skip first whitespace
357         }
358
359         // Expand the rest of the line
360         while (*s != EOS)
361         {
362                 // Copy single character
363                 if (*s != '\\')
364                 {
365                         if (dst >= edst)
366                                 goto overflow;
367
368                         // Skip comments in case a loose @ or \ is in there
369                         // In that case the tokeniser was trying to expand it.
370                         if ((*s == ';') || ((*s == '/') && (*(s + 1) == '/')))
371                                 goto skipcomments;
372
373                         *dst++ = *s++;
374                 }
375                 // Do macro expansion
376                 else
377                 {
378                         questmark = 0;
379
380                         // Do special cases
381                         switch (*++s)
382                         {
383                         case '\\':                                              // \\, \ (collapse to single backslash)
384                                 if (dst >= edst)
385                                         goto overflow;
386
387                                 *dst++ = *s++;
388                                 continue;
389                         case '?':                                               // \? <macro>  set `questmark' flag
390                                 ++s;
391                                 questmark = 1;
392                                 break;
393                         case '#':                                               // \#, number of arguments
394                                 sprintf(numbuf, "%d", (int)imacro->im_nargs);
395                                 goto copystr;
396                         case '!':                                               // \! size suffix supplied on invocation
397                                 switch ((int)imacro->im_siz)
398                                 {
399                                 case SIZN: d = "";   break;
400                                 case SIZB: d = ".b"; break;
401                                 case SIZW: d = ".w"; break;
402                                 case SIZL: d = ".l"; break;
403                                 }
404
405                                 goto copy_d;
406                         case '~':                                               // ==> unique label string Mnnnn...
407                                 sprintf(numbuf, "M%u", curuniq);
408 copystr:
409                                 d = numbuf;
410 copy_d:
411                                 s++;
412
413                                 while (*d != EOS)
414                                 {
415                                         if (dst >= edst)
416                                                 goto overflow;
417                                         else
418                                                 *dst++ = *d++;
419                                 }
420
421                                 continue;
422                         case EOS:
423                                 return error("missing argument name");
424                         }
425
426                         // \n ==> argument number 'n', 0..9
427                         if (chrtab[*s] & DIGIT)
428                         {
429                                 i = *s++ - '1';
430
431                                 if (i < 0)
432                                         i = 9;
433
434                                 goto arg_num;
435                         }
436
437                         // Get argument name: \name, \{name}
438                         d = mname;
439
440                         // \label
441                         if (*s != '{')
442                         {
443                                 do
444                                 {
445                                         *d++ = *s++;
446                                 }
447                                 while (chrtab[*s] & CTSYM);
448                         }
449                         // \\{label}
450                         else
451                         {
452                                 for(++s; *s != EOS && *s != '}';)
453                                         *d++ = *s++;
454
455                                 if (*s != '}')
456                                         return error("missing closing brace ('}')");
457                                 else
458                                         s++;
459                         }
460
461                         *d = EOS;
462
463                         // Lookup the argument and copy its (string) value into the
464                         // destination string
465                         DEBUG { printf("argument='%s'\n", mname); }
466
467                         if ((arg = lookup(mname, MACARG, macnum)) == NULL)
468                                 return error("undefined argument: '%s'", mname);
469                         else
470                         {
471                                 // Convert a string of tokens (terminated with EOL) back into
472                                 // text. If an argument is out of range (not specified in the
473                                 // macro invocation) then it is ignored.
474                                 i = (int)arg->svalue;
475 arg_num:
476                                 DEBUG { printf("~argnumber=%d (argBase=%u)\n", i, imacro->argBase); }
477                                 tk = NULL;
478
479                                 if (i < imacro->im_nargs)
480                                 {
481 #if 0
482 //                                      tk = argp[i];
483 //                                      tk = argPtrs[i];
484                                         tk = argPtrs[imacro->argBase + i];
485 #else
486                                         tk = imacro->argument[i].token;
487                                         symbolString = imacro->argument[i].string;
488 //DEBUG
489 //{
490 //      printf("ExM: Preparing to parse argument #%u...\n", i);
491 //      dumptok(tk);
492 //}
493 #endif
494                                 }
495
496                                 // \?arg yields:
497                                 //    0  if the argument is empty or non-existant,
498                                 //    1  if the argument is not empty
499                                 if (questmark)
500                                 {
501                                         if (tk == NULL || *tk == EOL)
502                                                 questmark = 0;
503
504                                         if (dst >= edst)
505                                                 goto overflow;
506
507                                         *dst++ = (char)(questmark + '0');
508                                         continue;
509                                 }
510
511                                 // Argument # is in range, so expand it
512                                 if (tk != NULL)
513                                 {
514                                         while (*tk != EOL)
515                                         {
516                                                 // Reverse-translation from a token number to a string.
517                                                 // This is a hack. It might be better table-driven.
518                                                 d = NULL;
519
520                                                 if ((*tk >= KW_D0) && !rdsp && !rgpu)
521                                                 {
522                                                         d = regname[(int)*tk++ - KW_D0];
523                                                         goto strcopy;
524                                                 }
525                                                 else if ((*tk >= KW_R0) && (*tk <= KW_R31))
526                                                 {
527                                                         d = riscregname[(int)*tk++ - KW_R0];
528                                                         goto strcopy;
529                                                 }
530                                                 else
531                                                 {
532                                                         switch ((int)*tk++)
533                                                         {
534                                                         case SYMBOL:
535 #if 0
536 //                                                              d = (char *)*tk++;
537                                                                 d = string[*tk++];
538 #else
539                                                                 // This fix should be done for strings too
540                                                                 d = symbolString[*tk++];
541 DEBUG { printf("ExM: SYMBOL=\"%s\"", d); }
542 #endif
543                                                                 break;
544                                                         case STRING:
545 #if 0
546 //                                                              d = (char *)*tk++;
547                                                                 d = string[*tk++];
548 #else
549                                                                 d = symbolString[*tk++];
550 #endif
551                                                                 if (dst >= edst)
552                                                                         goto overflow;
553
554                                                                 *dst++ = '"';
555
556                                                                 while (*d != EOS)
557                                                                 {
558                                                                         if (dst >= edst)
559                                                                                 goto overflow;
560                                                                         else
561                                                                                 *dst++ = *d++;
562                                                                 }
563
564                                                                 if (dst >= edst)
565                                                                         goto overflow;
566
567                                                                 *dst++ = '"';
568                                                                 continue;
569                                                                 break;
570 // Shamus: Changing the format specifier from %lx to %ux caused the assembler
571 //         to choke on legitimate code... Need to investigate this further
572 //         before changing anything else here!
573                                                         case CONST:
574                                                                 sprintf(numbuf, "$%lx", (long unsigned int)*tk++);
575                                                                 d = numbuf;
576                                                                 break;
577                                                         case DEQUALS:
578                                                                 d = "==";
579                                                                 break;
580                                                         case SET:
581                                                                 d = "set";
582                                                                 break;
583                                                         case COLON:
584                                                                 d = ":";
585                                                                 break;
586                                                         case DCOLON:
587                                                                 d = "::";
588                                                                 break;
589                                                         case GE:
590                                                                 d = ">=";
591                                                                 break;
592                                                         case LE:
593                                                                 d = "<=";
594                                                                 break;
595                                                         case NE:
596                                                                 d = "<>";
597                                                                 break;
598                                                         case SHR:
599                                                                 d = ">>";
600                                                                 break;
601                                                         case SHL:
602                                                                 d = "<<";
603                                                                 break;
604                                                         case DOTB:
605                                                                 d = ".b";
606                                                                 break;
607                                                         case DOTW:
608                                                                 d = ".w";
609                                                                 break;
610                                                         case DOTL:
611                                                                 d = ".l";
612                                                                 break;
613                                                         case CR_ABSCOUNT:
614                                                                 d = "^^abscount";
615                                                                 break;
616                                                         case CR_DATE:
617                                                                 d = "^^date";
618                                                                 break;
619                                                         case CR_TIME:
620                                                                 d = "^^time";
621                                                                 break;
622                                                         case CR_DEFINED:
623                                                                 d = "^^defined ";
624                                                                 break;
625                                                         case CR_REFERENCED:
626                                                                 d = "^^referenced ";
627                                                                 break;
628                                                         case CR_STREQ:
629                                                                 d = "^^streq ";
630                                                                 break;
631                                                         case CR_MACDEF:
632                                                                 d = "^^macdef ";
633                                                                 break;
634                                                         default:
635                                                                 if (dst >= edst)
636                                                                         goto overflow;
637
638                                                                 *dst++ = (char)*(tk - 1);
639                                                                 break;
640                                                         }
641                                                 }
642
643                                                 // If 'd' != NULL, copy string to destination
644                                                 if (d != NULL)
645                                                 {
646 strcopy:
647                                                         DEBUG printf("d='%s'\n", d);
648
649                                                         while (*d != EOS)
650                                                         {
651                                                                 if (dst >= edst)
652                                                                         goto overflow;
653                                                                 else
654                                                                         *dst++ = *d++;
655                                                         }
656                                                 }
657                                         }
658                                 }
659                         }
660                 }
661         }
662
663 skipcomments:
664
665         *dst = EOS;
666         DEBUG { printf("ExM: dst=\"%s\"\n", dest); }
667         return OK;
668
669 overflow:
670         *dst = EOS;
671         DEBUG { printf("*** OVERFLOW LINE ***\n%s\n", dest); }
672         return fatal("line too long as a result of macro expansion");
673 }
674
675
676 //
677 // Get next line of text from a macro
678 //
679 char * GetNextMacroLine(void)
680 {
681         IMACRO * imacro = cur_inobj->inobj.imacro;
682 //      LONG * strp = imacro->im_nextln;
683         struct LineList * strp = imacro->im_nextln;
684
685         if (strp == NULL)                                               // End-of-macro
686                 return NULL;
687
688         imacro->im_nextln = strp->next;
689 //      ExpandMacro((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
690         ExpandMacro(strp->line, imacro->im_lnbuf, LNSIZ);
691
692         return imacro->im_lnbuf;
693 }
694
695
696 //
697 // Get next line of text from a repeat block
698 //
699 char * GetNextRepeatLine(void)
700 {
701         IREPT * irept = cur_inobj->inobj.irept;
702         LONG * strp = irept->ir_nextln;                 // initial null
703
704         // Do repeat at end of .rept block's string list
705         if (strp == NULL)
706         {
707                 DEBUG { printf("back-to-top-of-repeat-block count=%d\n", (int)irept->ir_count); }
708                 irept->ir_nextln = irept->ir_firstln;   // copy first line
709
710                 if (irept->ir_count-- == 0)
711                 {
712                         DEBUG { printf("end-repeat-block\n"); }
713                         return NULL;
714                 }
715
716                 strp = irept->ir_nextln;
717         }
718
719         strcpy(irbuf, (char *)(irept->ir_nextln + 1));
720         DEBUG printf("repeat line='%s'\n", irbuf);
721         irept->ir_nextln = (LONG *)*strp;
722
723         return irbuf;
724 }
725
726
727 //
728 // Include a source file used at the root, and for ".include" files
729 //
730 int include(int handle, char * fname)
731 {
732         // Debug mode
733         DEBUG { printf("[include: %s, cfileno=%u]\n", fname, cfileno); }
734
735         // Alloc and initialize include-descriptors
736         INOBJ * inobj = a_inobj(SRC_IFILE);
737         IFILE * ifile = inobj->inobj.ifile;
738
739         ifile->ifhandle = handle;                               // Setup file handle
740         ifile->ifind = ifile->ifcnt = 0;                // Setup buffer indices
741         ifile->ifoldlineno = curlineno;                 // Save old line number
742         ifile->ifoldfname = curfname;                   // Save old filename
743         ifile->ifno = cfileno;                                  // Save old file number
744
745         // NB: This *must* be preincrement, we're adding one to the filecount here!
746         cfileno = ++filecount;                                  // Compute NEW file number
747         curfname = strdup(fname);                               // Set current filename (alloc storage)
748         curlineno = 0;                                                  // Start on line zero
749
750         // Add another file to the file-record
751         FILEREC * fr = (FILEREC *)malloc(sizeof(FILEREC));
752         fr->frec_next = NULL;
753         fr->frec_name = curfname;
754
755         if (last_fr == NULL)
756                 filerec = fr;                                           // Add first filerec
757         else
758                 last_fr->frec_next = fr;                        // Append to list of filerecs
759
760         last_fr = fr;
761         DEBUG { printf("[include: curfname: %s, cfileno=%u]\n", curfname, cfileno); }
762
763         return OK;
764 }
765
766
767 //
768 // Pop the current input level
769 //
770 int fpop(void)
771 {
772         IFILE * ifile;
773         IMACRO * imacro;
774         LONG * p, * p1;
775         INOBJ * inobj = cur_inobj;
776
777         if (inobj != NULL)
778         {
779                 // Pop IFENT levels until we reach the conditional assembly context we
780                 // were at when the input object was entered.
781                 int numUnmatched = 0;
782
783                 while (ifent != inobj->in_ifent)
784                 {
785                         if (d_endif() != 0)             // Something bad happened during endif parsing?
786                                 return -1;                      // If yes, bail instead of getting stuck in a loop
787
788                         numUnmatched++;
789                 }
790
791                 // Give a warning to the user that we had to wipe their bum for them
792                 if (numUnmatched > 0)
793                         warn("missing %d .endif(s)", numUnmatched);
794
795                 tok = inobj->in_otok;           // Restore tok and otok
796                 etok = inobj->in_etok;
797
798                 switch (inobj->in_type)
799                 {
800                 case SRC_IFILE:                         // Pop and release an IFILE
801                         DEBUG { printf("[Leaving: %s]\n", curfname); }
802
803                         ifile = inobj->inobj.ifile;
804                         ifile->if_link = f_ifile;
805                         f_ifile = ifile;
806                         close(ifile->ifhandle);                 // Close source file
807 DEBUG { printf("[fpop (pre):  curfname=%s]\n", curfname); }
808                         curfname = ifile->ifoldfname;   // Set current filename
809 DEBUG { printf("[fpop (post): curfname=%s]\n", curfname); }
810 DEBUG { printf("[fpop: (pre)  cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno); }
811                         curlineno = ifile->ifoldlineno; // Set current line#
812                         DEBUG printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno);
813                         cfileno = ifile->ifno;                  // Restore current file number
814 DEBUG { printf("[fpop: (post) cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno); }
815                         break;
816                 case SRC_IMACRO:                                        // Pop and release an IMACRO
817                         imacro = inobj->inobj.imacro;
818                         imacro->im_link = f_imacro;
819                         f_imacro = imacro;
820                         break;
821                 case SRC_IREPT:                                         // Pop and release an IREPT
822                         DEBUG printf("dealloc IREPT\n");
823                         p = inobj->inobj.irept->ir_firstln;
824
825                         while (p != NULL)
826                         {
827                                 p1 = (LONG *)*p;
828                                 p = p1;
829                         }
830
831                         break;
832                 }
833
834                 cur_inobj = inobj->in_link;
835                 inobj->in_link = f_inobj;
836                 f_inobj = inobj;
837         }
838
839         return 0;
840 }
841
842
843 //
844 // Get line from file into buf, return NULL on EOF or ptr to the start of a
845 // null-term line
846 //
847 char * GetNextLine(void)
848 {
849         int i, j;
850         char * p, * d;
851         int readamt = -1;                                               // 0 if last read() yeilded 0 bytes
852         IFILE * fl = cur_inobj->inobj.ifile;
853
854         for(;;)
855         {
856                 // Scan for next end-of-line; handle stupid text formats by treating
857                 // \r\n the same as \n. (lone '\r' at end of buffer means we have to
858                 // check for '\n').
859                 d = &fl->ifbuf[fl->ifind];
860
861                 for(p=d, i=0, j=fl->ifcnt; i<j; i++, p++)
862                 {
863                         if (*p == '\r' || *p == '\n')
864                         {
865                                 i++;
866
867                                 if (*p == '\r')
868                                 {
869                                         if (i >= j)
870                                                 break;  // Need to read more, then look for '\n' to eat
871                                         else if (p[1] == '\n')
872                                                 i++;
873                                 }
874
875                                 // Cover up the newline with end-of-string sentinel
876                                 *p = '\0';
877
878                                 fl->ifind += i;
879                                 fl->ifcnt -= i;
880                                 return d;
881                         }
882                 }
883
884                 // Handle hanging lines by ignoring them (Input file is exhausted, no
885                 // \r or \n on last line)
886                 // Shamus: This is retarded. Never ignore any input!
887                 if (!readamt && fl->ifcnt)
888                 {
889 #if 0
890                         fl->ifcnt = 0;
891                         *p = '\0';
892                         return NULL;
893 #else
894                         // Really should check to see if we're at the end of the buffer!
895                         // :-P
896                         fl->ifbuf[fl->ifind + fl->ifcnt] = '\0';
897                         fl->ifcnt = 0;
898                         return &fl->ifbuf[fl->ifind];
899 #endif
900                 }
901
902                 // Truncate and return absurdly long lines.
903                 if (fl->ifcnt >= QUANTUM)
904                 {
905                         fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0';
906                         fl->ifcnt = 0;
907                         return &fl->ifbuf[fl->ifind];
908                 }
909
910                 // Relocate what's left of a line to the beginning of the buffer, and
911                 // read some more of the file in; return NULL if the buffer's empty and
912                 // on EOF.
913                 if (fl->ifind != 0)
914                 {
915                         p = &fl->ifbuf[fl->ifind];
916                         d = &fl->ifbuf[fl->ifcnt & 1];
917
918                         for(i=0; i<fl->ifcnt; i++)
919                                 *d++ = *p++;
920
921                         fl->ifind = fl->ifcnt & 1;
922                 }
923
924                 readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM);
925
926                 if (readamt < 0)
927                         return NULL;
928
929                 if ((fl->ifcnt += readamt) == 0)
930                         return NULL;
931         }
932 }
933
934
935 //
936 // Tokenize a line
937 //
938 int TokenizeLine(void)
939 {
940         uint8_t * ln = NULL;            // Ptr to current position in line
941         uint8_t * p;                            // Random character ptr
942         TOKEN * tk;                                     // Token-deposit ptr
943         int state = 0;                          // State for keyword detector
944         int j = 0;                                      // Var for keyword detector
945         uint8_t c;                                      // Random char
946         VALUE v;                                        // Random value
947         uint8_t * nullspot = NULL;      // Spot to clobber for SYMBOL termination
948         int stuffnull;                          // 1:terminate SYMBOL '\0' at *nullspot
949         uint8_t c1;
950         int stringNum = 0;                      // Pointer to string locations in tokenized line
951
952 retry:
953
954         if (cur_inobj == NULL)                                  // Return EOF if input stack is empty
955                 return TKEOF;
956
957         // Get another line of input from the current input source: a file, a
958         // macro, or a repeat-block
959         switch (cur_inobj->in_type)
960         {
961         // Include-file:
962         // o  handle EOF;
963         // o  bump source line number;
964         // o  tag the listing-line with a space;
965         // o  kludge lines generated by Alcyon C.
966         case SRC_IFILE:
967                 if ((ln = GetNextLine()) == NULL)
968                 {
969 DEBUG { printf("TokenizeLine: Calling fpop() from SRC_IFILE...\n"); }
970                         if (fpop() == 0)                                // Pop input level
971                                 goto retry;                                     // Try for more lines
972                         else
973                         {
974                                 ifent->if_prev = (IFENT *) - 1; //Signal Assemble() that we have reached EOF with unbalanced if/endifs
975                                 return TKEOF;
976                         }
977                 }
978
979                 curlineno++;                                            // Bump line number
980                 lntag = SPACE;
981
982                 if (as68_flag)
983                 {
984                         // AS68 compatibility, throw away all lines starting with
985                         // back-quotes, tildes, or '*'
986                         // On other lines, turn the first '*' into a semi-colon.
987                         if (*ln == '`' || *ln == '~' || *ln == '*')
988                                 *ln = ';';
989                         else
990                         {
991                                 for(p=ln; *p!=EOS; p++)
992                                 {
993                                         if (*p == '*')
994                                         {
995                                                 *p = ';';
996                                                 break;
997                                         }
998                                 }
999                         }
1000                 }
1001
1002                 break;
1003         // Macro-block:
1004         // o  Handle end-of-macro;
1005         // o  tag the listing-line with an at (@) sign.
1006         case SRC_IMACRO:
1007                 if ((ln = GetNextMacroLine()) == NULL)
1008                 {
1009                         if (ExitMacro() == 0)                   // Exit macro (pop args, do fpop(), etc)
1010                                 goto retry;                                     // Try for more lines...
1011                         else
1012                                 return TKEOF;                           // Oops, we got a non zero return code, signal EOF
1013                 }
1014
1015                 lntag = '@';
1016                 break;
1017         // Repeat-block:
1018         // o  Handle end-of-repeat-block;
1019         // o  tag the listing-line with a pound (#) sign.
1020         case SRC_IREPT:
1021                 if ((ln = GetNextRepeatLine()) == NULL)
1022                 {
1023 DEBUG { printf("TokenizeLine: Calling fpop() from SRC_IREPT...\n"); }
1024                         fpop();
1025                         goto retry;
1026                 }
1027
1028                 lntag = '#';
1029                 break;
1030         }
1031
1032         // Save text of the line.  We only do this during listings and within
1033         // macro-type blocks, since it is expensive to unconditionally copy every
1034         // line.
1035         if (lnsave)
1036                 strcpy(lnbuf, ln);
1037
1038         // General house-keeping
1039         tok = tokeol;                   // Set "tok" to EOL in case of error
1040         tk = etok;                              // Reset token ptr
1041         stuffnull = 0;                  // Don't stuff nulls
1042         totlines++;                             // Bump total #lines assembled
1043
1044         // See if the entire line is a comment. This is a win if the programmer
1045         // puts in lots of comments
1046         if (*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln + 1) == '/')))
1047                 goto goteol;
1048
1049         // Main tokenization loop;
1050         //  o  skip whitespace;
1051         //  o  handle end-of-line;
1052         //  o  handle symbols;
1053         //  o  handle single-character tokens (operators, etc.);
1054         //  o  handle multiple-character tokens (constants, strings, etc.).
1055         for(; *ln!=EOS;)
1056         {
1057                 // Skip whitespace, handle EOL
1058                 while (chrtab[*ln] & WHITE)
1059                         ln++;
1060
1061                 // Handle EOL, comment with ';'
1062                 if (*ln == EOS || *ln == ';'|| ((*ln == '/') && (*(ln + 1) == '/')))
1063                         break;
1064
1065                 // Handle start of symbol. Symbols are null-terminated in place. The
1066                 // termination is always one symbol behind, since there may be no place
1067                 // for a null in the case that an operator immediately follows the name.
1068                 c = chrtab[*ln];
1069
1070                 if (c & STSYM)
1071                 {
1072                         if (stuffnull)                  // Terminate old symbol from previous pass
1073                                 *nullspot = EOS;
1074
1075                         v = 0;                                  // Assume no DOT attrib follows symbol
1076                         stuffnull = 1;
1077
1078                         // In some cases, we need to check for a DOTx at the *beginning*
1079                         // of a symbol, as the "start" of the line we're currently looking
1080                         // at could be somewhere in the middle of that line!
1081                         if (*ln == '.')
1082                         {
1083                                 // Make sure that it's *only* a .[bwsl] following, and not the
1084                                 // start of a local symbol:
1085                                 if ((chrtab[*(ln + 1)] & DOT)
1086                                         && (dotxtab[*(ln + 1)] != 0)
1087                                         && !(chrtab[*(ln + 2)] & CTSYM))
1088                                 {
1089                                         // We found a legitimate DOTx construct, so add it to the
1090                                         // token stream:
1091                                         ln++;
1092                                         stuffnull = 0;
1093                                         *tk++ = (TOKEN)dotxtab[*ln++];
1094                                         continue;
1095                                 }
1096                         }
1097
1098                         p = nullspot = ln++;    // Nullspot -> start of this symbol
1099
1100                         // Find end of symbol (and compute its length)
1101                         for(j=1; (int)chrtab[*ln]&CTSYM; j++)
1102                                 ln++;
1103
1104                         // Handle "DOT" special forms (like ".b") that follow a normal
1105                         // symbol or keyword:
1106                         if (*ln == '.')
1107                         {
1108                                 *ln++ = EOS;            // Terminate symbol
1109                                 stuffnull = 0;          // And never try it again
1110
1111                                 // Character following the `.' must have a DOT attribute, and
1112                                 // the chararacter after THAT one must not have a start-symbol
1113                                 // attribute (to prevent symbols that look like, for example,
1114                                 // "zingo.barf", which might be a good idea anyway....)
1115                                 if (((chrtab[*ln] & DOT) == 0) || (dotxtab[*ln] == 0))
1116                                         return error("[bwsl] must follow '.' in symbol");
1117
1118                                 v = (VALUE)dotxtab[*ln++];
1119
1120                                 if (chrtab[*ln] & CTSYM)
1121                                         return error("misuse of '.'; not allowed in symbols");
1122                         }
1123
1124                         // If the symbol is small, check to see if it's really the name of
1125                         // a register.
1126                         if (j <= KWSIZE)
1127                         {
1128                                 for(state=0; state>=0;)
1129                                 {
1130                                         j = (int)tolowertab[*p++];
1131                                         j += kwbase[state];
1132
1133                                         if (kwcheck[j] != state)
1134                                         {
1135                                                 j = -1;
1136                                                 break;
1137                                         }
1138
1139                                         if (*p == EOS || p == ln)
1140                                         {
1141                                                 j = kwaccept[j];
1142                                                 break;
1143                                         }
1144
1145                                         state = kwtab[j];
1146                                 }
1147                         }
1148                         else
1149                         {
1150                                 j = -1;
1151                         }
1152
1153                         // Make j = -1 if user tries to use a RISC register while in 68K mode
1154                         if (!(rgpu || rdsp) && ((TOKEN)j >= KW_R0 && (TOKEN)j <= KW_R31))
1155                         {
1156                                 j = -1;
1157                         }
1158
1159                         // Make j = -1 if time, date etc with no preceeding ^^
1160                         // defined, referenced, streq, macdef, date and time
1161                         switch ((TOKEN)j)
1162                         {
1163                         case 112:   // defined
1164                         case 113:   // referenced
1165                         case 118:   // streq
1166                         case 119:   // macdef
1167                         case 120:   // time
1168                         case 121:   // date
1169                                 j = -1;
1170                         }
1171
1172                         // If not tokenized keyword OR token was not found
1173                         if ((j < 0) || (state < 0))
1174                         {
1175                                 *tk++ = SYMBOL;
1176 //#warning
1177 //problem here: nullspot is a char * but TOKEN is a uint32_t. On a 64-bit
1178 //system, this will cause all kinds of mischief.
1179 #if 0
1180                                 *tk++ = (TOKEN)nullspot;
1181 #else
1182                                 string[stringNum] = nullspot;
1183                                 *tk++ = stringNum;
1184                                 stringNum++;
1185 #endif
1186                         }
1187                         else
1188                         {
1189                                 *tk++ = (TOKEN)j;
1190                                 stuffnull = 0;
1191                         }
1192
1193                         if (v)                                                  // Record attribute token (if any)
1194                                 *tk++ = (TOKEN)v;
1195
1196                         if (stuffnull)                                  // Arrange for string termination on next pass
1197                                 nullspot = ln;
1198
1199                         continue;
1200                 }
1201
1202                 // Handle identity tokens
1203                 if (c & SELF)
1204                 {
1205                         *tk++ = *ln++;
1206                         continue;
1207                 }
1208
1209                 // Handle multiple-character tokens
1210                 if (c & MULTX)
1211                 {
1212                         switch (*ln++)
1213                         {
1214                         case '!':               // ! or !=
1215                                 if (*ln == '=')
1216                                 {
1217                                         *tk++ = NE;
1218                                         ++ln;
1219                                 }
1220                                 else
1221                                         *tk++ = '!';
1222
1223                                 continue;
1224                         case '\'':              // 'string'
1225                                 if (m6502)
1226                                 {
1227                                         // Hardcoded for now, maybe this will change in the future
1228                                         *tk++ = STRINGA8;
1229                                         goto dostring;
1230                                 }
1231                                 // Fall through
1232                         case '\"':              // "string"
1233                                 *tk++ = STRING;
1234 dostring:
1235                                 c1 = ln[-1];
1236                                 string[stringNum] = ln;
1237                                 *tk++ = stringNum;
1238                                 stringNum++;
1239
1240                                 for(p=ln; *ln!=EOS && *ln!=c1;)
1241                                 {
1242                                         c = *ln++;
1243
1244                                         if (c == '\\')
1245                                         {
1246                                                 switch (*ln++)
1247                                                 {
1248                                                 case EOS:
1249                                                         return(error("unterminated string"));
1250                                                 case 'e':
1251                                                         c = '\033';
1252                                                         break;
1253                                                 case 'n':
1254                                                         c = '\n';
1255                                                         break;
1256                                                 case 'b':
1257                                                         c = '\b';
1258                                                         break;
1259                                                 case 't':
1260                                                         c = '\t';
1261                                                         break;
1262                                                 case 'r':
1263                                                         c = '\r';
1264                                                         break;
1265                                                 case 'f':
1266                                                         c = '\f';
1267                                                         break;
1268                                                 case '\"':
1269                                                         c = '\"';
1270                                                         break;
1271                                                 case '\'':
1272                                                         c = '\'';
1273                                                         break;
1274                                                 case '\\':
1275                                                         c = '\\';
1276                                                         break;
1277                                                 default:
1278                                                         warn("bad backslash code in string");
1279                                                         ln--;
1280                                                         break;
1281                                                 }
1282                                         }
1283
1284                                         *p++ = c;
1285                                 }
1286
1287                                 if (*ln++ != c1)
1288                                         return error("unterminated string");
1289
1290                                 *p++ = EOS;
1291                                 continue;
1292                         case '$':               // $, hex constant
1293                                 if (chrtab[*ln] & HDIGIT)
1294                                 {
1295                                         v = 0;
1296
1297                                         // Parse the hex value
1298                                         while (hextab[*ln] >= 0)
1299                                                 v = (v << 4) + (int)hextab[*ln++];
1300
1301                                         if (*ln == '.')
1302                                         {
1303                                                 if (obj_format == BSD)
1304                                                 {
1305                                                         if ((*(ln + 1) & 0xDF) == 'B')
1306                                                         {
1307                                                                 v &= 0x000000FF;
1308                                                                 ln += 2;
1309                                                         }
1310                                                         else if ((*(ln + 1) & 0xDF) == 'W')
1311                                                         {
1312                                                                 v &= 0x0000FFFF;
1313                                                                 ln += 2;
1314                                                         }
1315                                                         else if ((*(ln + 1) & 0xDF) == 'L')
1316                                                         {
1317                                                                 ln += 2;
1318                                                         }
1319                                                 }
1320                                         }
1321
1322                                         *tk++ = CONST;
1323                                         *tk++ = v;
1324
1325                                         if (obj_format == ALCYON)
1326                                         {
1327                                                 if (*ln == '.')
1328                                                 {
1329                                                         if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1330                                                         {
1331                                                                 *tk++ = DOTW;
1332                                                                 ln += 2;
1333                                                         }
1334                                                         else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1335                                                         {
1336                                                                 *tk++ = DOTL;
1337                                                                 ln += 2;
1338                                                         }
1339                                                 }
1340                                         }
1341                                 }
1342                                 else
1343                                         *tk++ = '$';
1344
1345                                 continue;
1346                         case '<':               // < or << or <> or <=
1347                                 switch (*ln)
1348                                 {
1349                                 case '<':
1350                                         *tk++ = SHL;
1351                                         ++ln;
1352                                         continue;
1353                                 case '>':
1354                                         *tk++ = NE;
1355                                         ++ln;
1356                                         continue;
1357                                 case '=':
1358                                         *tk++ = LE;
1359                                         ++ln;
1360                                         continue;
1361                                 default:
1362                                         *tk++ = '<';
1363                                         continue;
1364                                 }
1365                         case ':':               // : or ::
1366                                 if (*ln == ':')
1367                                 {
1368                                         *tk++ = DCOLON;
1369                                         ++ln;
1370                                 }
1371                                 else
1372                                         *tk++ = ':';
1373
1374                                 continue;
1375                         case '=':               // = or ==
1376                                 if (*ln == '=')
1377                                 {
1378                                         *tk++ = DEQUALS;
1379                                         ++ln;
1380                                 }
1381                                 else
1382                                         *tk++ = '=';
1383
1384                                 continue;
1385                         case '>':               // > or >> or >=
1386                                 switch (*ln)
1387                                 {
1388                                 case '>':
1389                                         *tk++ = SHR;
1390                                         ln++;
1391                                         continue;
1392                                 case '=':
1393                                         *tk++ = GE;
1394                                         ln++;
1395                                         continue;
1396                                 default:
1397                                         *tk++ = '>';
1398                                         continue;
1399                                 }
1400                         case '%':               // % or binary constant
1401                                 if (*ln < '0' || *ln > '1')
1402                                 {
1403                                         *tk++ = '%';
1404                                         continue;
1405                                 }
1406
1407                                 v = 0;
1408
1409                                 while (*ln >= '0' && *ln <= '1')
1410                                         v = (v << 1) + *ln++ - '0';
1411
1412                                 if (*ln == '.')
1413                                 {
1414                                         if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1415                                         {
1416                                                 v &= 0x000000FF;
1417                                                 ln += 2;
1418                                         }
1419
1420                                         if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1421                                         {
1422                                                 v &= 0x0000FFFF;
1423                                                 ln += 2;
1424                                         }
1425
1426                                         if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1427                                         {
1428                                                 ln += 2;
1429                                         }
1430                                 }
1431
1432                                 *tk++ = CONST;
1433                                 *tk++ = v;
1434                                 continue;
1435                         case '@':               // @ or octal constant
1436                                 if (*ln < '0' || *ln > '7')
1437                                 {
1438                                         *tk++ = '@';
1439                                         continue;
1440                                 }
1441
1442                                 v = 0;
1443
1444                                 while (*ln >= '0' && *ln <= '7')
1445                                         v = (v << 3) + *ln++ - '0';
1446
1447                                 if (*ln == '.')
1448                                 {
1449                                         if ((*(ln+1) == 'b') || (*(ln+1) == 'B'))
1450                                         {
1451                                                 v &= 0x000000FF;
1452                                                 ln += 2;
1453                                         }
1454
1455                                         if ((*(ln+1) == 'w') || (*(ln+1) == 'W'))
1456                                         {
1457                                                 v &= 0x0000FFFF;
1458                                                 ln += 2;
1459                                         }
1460
1461                                         if ((*(ln+1) == 'l') || (*(ln+1) == 'L'))
1462                                         {
1463                                                 ln += 2;
1464                                         }
1465                                 }
1466
1467                                 *tk++ = CONST;
1468                                 *tk++ = v;
1469                                 continue;
1470                         case '^':               // ^ or ^^ <operator-name>
1471                                 if (*ln != '^')
1472                                 {
1473                                         *tk++ = '^';
1474                                         continue;
1475                                 }
1476
1477                                 if (((int)chrtab[*++ln] & STSYM) == 0)
1478                                 {
1479                                         error("invalid symbol following ^^");
1480                                         continue;
1481                                 }
1482
1483                                 p = ln++;
1484
1485                                 while ((int)chrtab[*ln] & CTSYM)
1486                                         ++ln;
1487
1488                                 for(state=0; state>=0;)
1489                                 {
1490                                         // Get char, convert to lowercase
1491                                         j = *p++;
1492
1493                                         if (j >= 'A' && j <= 'Z')
1494                                                 j += 0x20;
1495
1496                                         j += kwbase[state];
1497
1498                                         if (kwcheck[j] != state)
1499                                         {
1500                                                 j = -1;
1501                                                 break;
1502                                         }
1503
1504                                         if (*p == EOS || p == ln)
1505                                         {
1506                                                 j = kwaccept[j];
1507                                                 break;
1508                                         }
1509
1510                                         state = kwtab[j];
1511                                 }
1512
1513                                 if (j < 0 || state < 0)
1514                                 {
1515                                         error("unknown symbol following ^^");
1516                                         continue;
1517                                 }
1518
1519                                 *tk++ = (TOKEN)j;
1520                                 continue;
1521                         default:
1522                                 interror(2);    // Bad MULTX entry in chrtab
1523                                 continue;
1524                         }
1525                 }
1526
1527                 // Handle decimal constant
1528                 if (c & DIGIT)
1529                 {
1530                         v = 0;
1531
1532                         while ((int)chrtab[*ln] & DIGIT)
1533                                 v = (v * 10) + *ln++ - '0';
1534
1535                         // See if there's a .[bwl] after the constant & deal with it if so
1536                         if (*ln == '.')
1537                         {
1538                                 if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1539                                 {
1540                                         v &= 0x000000FF;
1541                                         ln += 2;
1542                                 }
1543                                 else if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1544                                 {
1545                                         v &= 0x0000FFFF;
1546                                         ln += 2;
1547                                 }
1548                                 else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1549                                 {
1550                                         ln += 2;
1551                                 }
1552                         }
1553
1554                         *tk++ = CONST;
1555                         *tk++ = v;
1556 //printf("CONST: %i\n", v);
1557                         continue;
1558                 }
1559
1560                 // Handle illegal character
1561                 return error("illegal character $%02X found", *ln);
1562         }
1563
1564         // Terminate line of tokens and return "success."
1565
1566 goteol:
1567         tok = etok;                                                             // Set tok to beginning of line
1568
1569         if (stuffnull)                                                  // Terminate last SYMBOL
1570                 *nullspot = EOS;
1571
1572         *tk++ = EOL;
1573
1574         return OK;
1575 }
1576
1577
1578 //
1579 // .GOTO <label>        goto directive
1580 //
1581 // The label is searched for starting from the first line of the current,
1582 // enclosing macro definition. If no enclosing macro exists, an error is
1583 // generated.
1584 //
1585 // A label is of the form:
1586 //
1587 // :<name><whitespace>
1588 //
1589 // The colon must appear in column 1.  The label is stripped prior to macro
1590 // expansion, and is NOT subject to macro expansion.  The whitespace may also
1591 // be EOL.
1592 //
1593 int d_goto(WORD unused)
1594 {
1595         // Setup for the search
1596         if (*tok != SYMBOL)
1597                 return error("missing label");
1598
1599         char * sym = string[tok[1]];
1600         tok += 2;
1601
1602         if (cur_inobj->in_type != SRC_IMACRO)
1603                 return error("goto not in macro");
1604
1605         IMACRO * imacro = cur_inobj->inobj.imacro;
1606         struct LineList * defln = imacro->im_macro->lineList;
1607
1608         // Attempt to find the label, starting with the first line.
1609         for(; defln!=NULL; defln=defln->next)
1610         {
1611                 // Must start with a colon
1612                 if (defln->line[0] == ':')
1613                 {
1614                         // Compare names (sleazo string compare)
1615                         char * s1 = sym;
1616                         char * s2 = defln->line;
1617
1618                         // Either we will match the strings to EOS on both, or we will
1619                         // match EOS on string 1 to whitespace on string 2. Otherwise, we
1620                         // have no match.
1621                         while ((*s1 == *s2) || ((*s1 == EOS) && (chrtab[*s2] & WHITE)))
1622                         {
1623                                 // If we reached the end of string 1 (sym), we're done.
1624                                 // Note that we're also checking for the end of string 2 as
1625                                 // well, since we've established they're equal above.
1626                                 if (*s1 == EOS)
1627                                 {
1628                                         // Found the label, set new macro next-line and return.
1629                                         imacro->im_nextln = defln;
1630                                         return 0;
1631                                 }
1632
1633                                 s1++;
1634                                 s2++;
1635                         }
1636                 }
1637         }
1638
1639         return error("goto label not found");
1640 }
1641
1642
1643 void DumpTokenBuffer(void)
1644 {
1645         TOKEN * t;
1646         printf("Tokens [%X]: ", sloc);
1647
1648         for(t=tokbuf; *t!=EOL; t++)
1649         {
1650                 if (*t == COLON)
1651                         printf("[COLON]");
1652                 else if (*t == CONST)
1653                 {
1654                         t++;
1655                         printf("[CONST: $%X]", (uint32_t)*t);
1656                 }
1657                 else if (*t == ACONST)
1658                         printf("[ACONST]");
1659                 else if (*t == STRING)
1660                 {
1661                         t++;
1662                         printf("[STRING:\"%s\"]", string[*t]);
1663                 }
1664                 else if (*t == SYMBOL)
1665                 {
1666                         t++;
1667                         printf("[SYMBOL:\"%s\"]", string[*t]);
1668                 }
1669                 else if (*t == EOS)
1670                         printf("[EOS]");
1671                 else if (*t == TKEOF)
1672                         printf("[TKEOF]");
1673                 else if (*t == DEQUALS)
1674                         printf("[DEQUALS]");
1675                 else if (*t == SET)
1676                         printf("[SET]");
1677                 else if (*t == REG)
1678                         printf("[REG]");
1679                 else if (*t == DCOLON)
1680                         printf("[DCOLON]");
1681                 else if (*t == GE)
1682                         printf("[GE]");
1683                 else if (*t == LE)
1684                         printf("[LE]");
1685                 else if (*t == NE)
1686                         printf("[NE]");
1687                 else if (*t == SHR)
1688                         printf("[SHR]");
1689                 else if (*t == SHL)
1690                         printf("[SHL]");
1691                 else if (*t == UNMINUS)
1692                         printf("[UNMINUS]");
1693                 else if (*t == DOTB)
1694                         printf("[DOTB]");
1695                 else if (*t == DOTW)
1696                         printf("[DOTW]");
1697                 else if (*t == DOTL)
1698                         printf("[DOTL]");
1699                 else if (*t == DOTI)
1700                         printf("[DOTI]");
1701                 else if (*t == ENDEXPR)
1702                         printf("[ENDEXPR]");
1703                 else if (*t == CR_ABSCOUNT)
1704                         printf("[CR_ABSCOUNT]");
1705                 else if (*t == CR_DEFINED)
1706                         printf("[CR_DEFINED]");
1707                 else if (*t == CR_REFERENCED)
1708                         printf("[CR_REFERENCED]");
1709                 else if (*t == CR_STREQ)
1710                         printf("[CR_STREQ]");
1711                 else if (*t == CR_MACDEF)
1712                         printf("[CR_MACDEF]");
1713                 else if (*t == CR_TIME)
1714                         printf("[CR_TIME]");
1715                 else if (*t == CR_DATE)
1716                         printf("[CR_DATE]");
1717                 else if (*t >= 0x20 && *t <= 0x2F)
1718                         printf("[%c]", (char)*t);
1719                 else if (*t >= 0x3A && *t <= 0x3F)
1720                         printf("[%c]", (char)*t);
1721                 else if (*t >= 0x80 && *t <= 0x87)
1722                         printf("[D%u]", ((uint32_t)*t) - 0x80);
1723                 else if (*t >= 0x88 && *t <= 0x8F)
1724                         printf("[A%u]", ((uint32_t)*t) - 0x88);
1725                 else
1726                         printf("[%X:%c]", (uint32_t)*t, (char)*t);
1727         }
1728
1729         printf("[EOL]\n");
1730 }
1731