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