]> Shamusworld >> Repos - rmac/blob - token.c
Fixed IREPT blocks to not corrupt memory on 64-bit hosts.
[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
286         case SRC_IMACRO:                                                // Alloc and init an IMACRO
287                 if (f_imacro == NULL)
288                         imacro = malloc(sizeof(IMACRO));
289                 else
290                 {
291                         imacro = f_imacro;
292                         f_imacro = f_imacro->im_link;
293                 }
294
295                 inobj->inobj.imacro = imacro;
296                 break;
297
298         case SRC_IREPT:                                                 // Alloc and init an IREPT
299                 inobj->inobj.irept = malloc(sizeof(IREPT));
300                 DEBUG { printf("alloc IREPT\n"); }
301                 break;
302         }
303
304         // Install INOBJ on top of input stack
305         inobj->in_ifent = ifent;                                // Record .if context on entry
306         inobj->in_type = (WORD)typ;
307         inobj->in_otok = tok;
308         inobj->in_etok = etok;
309         inobj->in_link = cur_inobj;
310         cur_inobj = inobj;
311
312         return inobj;
313 }
314
315
316 //
317 // Perform macro substitution from 'orig' to 'dest'. Return OK or some error.
318 // A macro reference is in one of two forms:
319 // \name <non-name-character>
320 // \{name}
321 // A doubled backslash (\\) is compressed to a single backslash (\).
322 // Argument definitions have been pre-tokenized, so we have to turn them back
323 // into text. This means that numbers, in particular, become hex, regardless of
324 // their representation when the macro was invoked. This is a hack.
325 // A label may appear at the beginning of the line:
326 // :<name><whitespace>
327 // (the colon must be in the first column). These labels are stripped before
328 // macro expansion takes place.
329 //
330 int ExpandMacro(char * src, char * dest, int destsiz)
331 {
332         int i;
333         int questmark;                  // \? for testing argument existence
334         char mname[128];                // Assume max size of a formal arg name
335         char numbuf[20];                // Buffer for text of CONSTs
336         TOKEN * tk;
337         SYM * arg;
338         char ** symbolString;
339
340         DEBUG { printf("ExM: src=\"%s\"\n", src); }
341
342         IMACRO * imacro = cur_inobj->inobj.imacro;
343         int macnum = (int)(imacro->im_macro->sattr);
344
345         char * dst = dest;                                              // Next dest slot
346         char * edst = dest + destsiz - 1;               // End + 1(?) of dest buffer
347
348         // Check for (and skip over) any "label" on the line
349         char * s = src;
350         char * d = NULL;
351
352         if (*s == ':')
353         {
354                 while (*s != EOS && !(chrtab[*s] & WHITE))
355                         s++;
356
357                 if (*s != EOS)
358                         s++;                                                    // Skip first whitespace
359         }
360
361         // Expand the rest of the line
362         while (*s != EOS)
363         {
364                 // Copy single character
365                 if (*s != '\\')
366                 {
367                         if (dst >= edst)
368                                 goto overflow;
369
370                         // Skip comments in case a loose @ or \ is in there
371                         // In that case the tokeniser was trying to expand it.
372                         if ((*s == ';') || ((*s == '/') && (*(s + 1) == '/')))
373                                 goto skipcomments;
374
375                         *dst++ = *s++;
376                 }
377                 // Do macro expansion
378                 else
379                 {
380                         questmark = 0;
381
382                         // Do special cases
383                         switch (*++s)
384                         {
385                         case '\\':                                              // \\, \ (collapse to single backslash)
386                                 if (dst >= edst)
387                                         goto overflow;
388
389                                 *dst++ = *s++;
390                                 continue;
391                         case '?':                                               // \? <macro>  set `questmark' flag
392                                 ++s;
393                                 questmark = 1;
394                                 break;
395                         case '#':                                               // \#, number of arguments
396                                 sprintf(numbuf, "%d", (int)imacro->im_nargs);
397                                 goto copystr;
398                         case '!':                                               // \! size suffix supplied on invocation
399                                 switch ((int)imacro->im_siz)
400                                 {
401                                 case SIZN: d = "";   break;
402                                 case SIZB: d = ".b"; break;
403                                 case SIZW: d = ".w"; break;
404                                 case SIZL: d = ".l"; break;
405                                 }
406
407                                 goto copy_d;
408                         case '~':                                               // ==> unique label string Mnnnn...
409                                 sprintf(numbuf, "M%u", curuniq);
410 copystr:
411                                 d = numbuf;
412 copy_d:
413                                 s++;
414
415                                 while (*d != EOS)
416                                 {
417                                         if (dst >= edst)
418                                                 goto overflow;
419                                         else
420                                                 *dst++ = *d++;
421                                 }
422
423                                 continue;
424                         case EOS:
425                                 return error("missing argument name");
426                         }
427
428                         // \n ==> argument number 'n', 0..9
429                         if (chrtab[*s] & DIGIT)
430                         {
431                                 i = *s++ - '1';
432
433                                 if (i < 0)
434                                         i = 9;
435
436                                 goto arg_num;
437                         }
438
439                         // Get argument name: \name, \{name}
440                         d = mname;
441
442                         // \label
443                         if (*s != '{')
444                         {
445                                 do
446                                 {
447                                         *d++ = *s++;
448                                 }
449                                 while (chrtab[*s] & CTSYM);
450                         }
451                         // \\{label}
452                         else
453                         {
454                                 for(++s; *s != EOS && *s != '}';)
455                                         *d++ = *s++;
456
457                                 if (*s != '}')
458                                         return error("missing closing brace ('}')");
459                                 else
460                                         s++;
461                         }
462
463                         *d = EOS;
464
465                         // Lookup the argument and copy its (string) value into the
466                         // destination string
467                         DEBUG { printf("argument='%s'\n", mname); }
468
469                         if ((arg = lookup(mname, MACARG, macnum)) == NULL)
470                                 return error("undefined argument: '%s'", mname);
471                         else
472                         {
473                                 // Convert a string of tokens (terminated with EOL) back into
474                                 // text. If an argument is out of range (not specified in the
475                                 // macro invocation) then it is ignored.
476                                 i = (int)arg->svalue;
477 arg_num:
478                                 DEBUG { printf("~argnumber=%d (argBase=%u)\n", i, imacro->argBase); }
479                                 tk = NULL;
480
481                                 if (i < imacro->im_nargs)
482                                 {
483 #if 0
484 //                                      tk = argp[i];
485 //                                      tk = argPtrs[i];
486                                         tk = argPtrs[imacro->argBase + i];
487 #else
488                                         tk = imacro->argument[i].token;
489                                         symbolString = imacro->argument[i].string;
490 //DEBUG
491 //{
492 //      printf("ExM: Preparing to parse argument #%u...\n", i);
493 //      dumptok(tk);
494 //}
495 #endif
496                                 }
497
498                                 // \?arg yields:
499                                 //    0  if the argument is empty or non-existant,
500                                 //    1  if the argument is not empty
501                                 if (questmark)
502                                 {
503                                         if (tk == NULL || *tk == EOL)
504                                                 questmark = 0;
505
506                                         if (dst >= edst)
507                                                 goto overflow;
508
509                                         *dst++ = (char)(questmark + '0');
510                                         continue;
511                                 }
512
513                                 // Argument # is in range, so expand it
514                                 if (tk != NULL)
515                                 {
516                                         while (*tk != EOL)
517                                         {
518                                                 // Reverse-translation from a token number to a string.
519                                                 // This is a hack. It might be better table-driven.
520                                                 d = NULL;
521
522                                                 if ((*tk >= KW_D0) && !rdsp && !rgpu)
523                                                 {
524                                                         d = regname[(int)*tk++ - KW_D0];
525                                                         goto strcopy;
526                                                 }
527                                                 else if ((*tk >= KW_R0) && (*tk <= KW_R31))
528                                                 {
529                                                         d = riscregname[(int)*tk++ - KW_R0];
530                                                         goto strcopy;
531                                                 }
532                                                 else
533                                                 {
534                                                         switch ((int)*tk++)
535                                                         {
536                                                         case SYMBOL:
537 #if 0
538 //                                                              d = (char *)*tk++;
539                                                                 d = string[*tk++];
540 #else
541                                                                 // This fix should be done for strings too
542                                                                 d = symbolString[*tk++];
543 DEBUG { printf("ExM: SYMBOL=\"%s\"", d); }
544 #endif
545                                                                 break;
546                                                         case STRING:
547 #if 0
548 //                                                              d = (char *)*tk++;
549                                                                 d = string[*tk++];
550 #else
551                                                                 d = symbolString[*tk++];
552 #endif
553                                                                 if (dst >= edst)
554                                                                         goto overflow;
555
556                                                                 *dst++ = '"';
557
558                                                                 while (*d != EOS)
559                                                                 {
560                                                                         if (dst >= edst)
561                                                                                 goto overflow;
562                                                                         else
563                                                                                 *dst++ = *d++;
564                                                                 }
565
566                                                                 if (dst >= edst)
567                                                                         goto overflow;
568
569                                                                 *dst++ = '"';
570                                                                 continue;
571                                                                 break;
572 // Shamus: Changing the format specifier from %lx to %ux caused the assembler
573 //         to choke on legitimate code... Need to investigate this further
574 //         before changing anything else here!
575                                                         case CONST:
576                                                                 sprintf(numbuf, "$%lx", (long unsigned int)*tk++);
577                                                                 d = numbuf;
578                                                                 break;
579                                                         case DEQUALS:
580                                                                 d = "==";
581                                                                 break;
582                                                         case SET:
583                                                                 d = "set";
584                                                                 break;
585                                                         case COLON:
586                                                                 d = ":";
587                                                                 break;
588                                                         case DCOLON:
589                                                                 d = "::";
590                                                                 break;
591                                                         case GE:
592                                                                 d = ">=";
593                                                                 break;
594                                                         case LE:
595                                                                 d = "<=";
596                                                                 break;
597                                                         case NE:
598                                                                 d = "<>";
599                                                                 break;
600                                                         case SHR:
601                                                                 d = ">>";
602                                                                 break;
603                                                         case SHL:
604                                                                 d = "<<";
605                                                                 break;
606                                                         case DOTB:
607                                                                 d = ".b";
608                                                                 break;
609                                                         case DOTW:
610                                                                 d = ".w";
611                                                                 break;
612                                                         case DOTL:
613                                                                 d = ".l";
614                                                                 break;
615                                                         case CR_ABSCOUNT:
616                                                                 d = "^^abscount";
617                                                                 break;
618                                                         case CR_DATE:
619                                                                 d = "^^date";
620                                                                 break;
621                                                         case CR_TIME:
622                                                                 d = "^^time";
623                                                                 break;
624                                                         case CR_DEFINED:
625                                                                 d = "^^defined ";
626                                                                 break;
627                                                         case CR_REFERENCED:
628                                                                 d = "^^referenced ";
629                                                                 break;
630                                                         case CR_STREQ:
631                                                                 d = "^^streq ";
632                                                                 break;
633                                                         case CR_MACDEF:
634                                                                 d = "^^macdef ";
635                                                                 break;
636                                                         default:
637                                                                 if (dst >= edst)
638                                                                         goto overflow;
639
640                                                                 *dst++ = (char)*(tk - 1);
641                                                                 break;
642                                                         }
643                                                 }
644
645                                                 // If 'd' != NULL, copy string to destination
646                                                 if (d != NULL)
647                                                 {
648 strcopy:
649                                                         DEBUG printf("d='%s'\n", d);
650
651                                                         while (*d != EOS)
652                                                         {
653                                                                 if (dst >= edst)
654                                                                         goto overflow;
655                                                                 else
656                                                                         *dst++ = *d++;
657                                                         }
658                                                 }
659                                         }
660                                 }
661                         }
662                 }
663         }
664
665 skipcomments:
666
667         *dst = EOS;
668         DEBUG { printf("ExM: dst=\"%s\"\n", dest); }
669         return OK;
670
671 overflow:
672         *dst = EOS;
673         DEBUG { printf("*** OVERFLOW LINE ***\n%s\n", dest); }
674         return fatal("line too long as a result of macro expansion");
675 }
676
677
678 //
679 // Get next line of text from a macro
680 //
681 char * GetNextMacroLine(void)
682 {
683         IMACRO * imacro = cur_inobj->inobj.imacro;
684 //      LONG * strp = imacro->im_nextln;
685         LLIST * strp = imacro->im_nextln;
686
687         if (strp == NULL)                                               // End-of-macro
688                 return NULL;
689
690         imacro->im_nextln = strp->next;
691 //      ExpandMacro((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
692         ExpandMacro(strp->line, imacro->im_lnbuf, LNSIZ);
693
694         return imacro->im_lnbuf;
695 }
696
697
698 //
699 // Get next line of text from a repeat block
700 //
701 char * GetNextRepeatLine(void)
702 {
703         IREPT * irept = cur_inobj->inobj.irept;
704 //      LONG * strp = irept->ir_nextln;                 // initial null
705
706         // Do repeat at end of .rept block's string list
707 //      if (strp == NULL)
708         if (irept->ir_nextln == NULL)
709         {
710                 DEBUG { printf("back-to-top-of-repeat-block count=%d\n", (int)irept->ir_count); }
711                 irept->ir_nextln = irept->ir_firstln;   // copy first line
712
713                 if (irept->ir_count-- == 0)
714                 {
715                         DEBUG { printf("end-repeat-block\n"); }
716                         return NULL;
717                 }
718
719 //              strp = irept->ir_nextln;
720         }
721
722 //      strcpy(irbuf, (char *)(irept->ir_nextln + 1));
723         strcpy(irbuf, irept->ir_nextln->line);
724         DEBUG { printf("repeat line='%s'\n", irbuf); }
725 //      irept->ir_nextln = (LONG *)*strp;
726         irept->ir_nextln = irept->ir_nextln->next;
727
728         return irbuf;
729 }
730
731
732 //
733 // Include a source file used at the root, and for ".include" files
734 //
735 int include(int handle, char * fname)
736 {
737         // Debug mode
738         DEBUG { printf("[include: %s, cfileno=%u]\n", fname, cfileno); }
739
740         // Alloc and initialize include-descriptors
741         INOBJ * inobj = a_inobj(SRC_IFILE);
742         IFILE * ifile = inobj->inobj.ifile;
743
744         ifile->ifhandle = handle;                       // Setup file handle
745         ifile->ifind = ifile->ifcnt = 0;        // Setup buffer indices
746         ifile->ifoldlineno = curlineno;         // Save old line number
747         ifile->ifoldfname = curfname;           // Save old filename
748         ifile->ifno = cfileno;                          // Save old file number
749
750         // NB: This *must* be preincrement, we're adding one to the filecount here!
751         cfileno = ++filecount;                          // Compute NEW file number
752         curfname = strdup(fname);                       // Set current filename (alloc storage)
753         curlineno = 0;                                          // Start on line zero
754
755         // Add another file to the file-record
756         FILEREC * fr = (FILEREC *)malloc(sizeof(FILEREC));
757         fr->frec_next = NULL;
758         fr->frec_name = curfname;
759
760         if (last_fr == NULL)
761                 filerec = fr;                                   // Add first filerec
762         else
763                 last_fr->frec_next = fr;                // Append to list of filerecs
764
765         last_fr = fr;
766         DEBUG { printf("[include: curfname: %s, cfileno=%u]\n", curfname, cfileno); }
767
768         return OK;
769 }
770
771
772 //
773 // Pop the current input level
774 //
775 int fpop(void)
776 {
777         INOBJ * inobj = cur_inobj;
778
779         if (inobj == NULL)
780                 return 0;
781
782         // Pop IFENT levels until we reach the conditional assembly context we
783         // were at when the input object was entered.
784         int numUnmatched = 0;
785
786         while (ifent != inobj->in_ifent)
787         {
788                 if (d_endif() != 0)     // Something bad happened during endif parsing?
789                         return -1;              // If yes, bail instead of getting stuck in a loop
790
791                 numUnmatched++;
792         }
793
794         // Give a warning to the user that we had to wipe their bum for them
795         if (numUnmatched > 0)
796                 warn("missing %d .endif(s)", numUnmatched);
797
798         tok = inobj->in_otok;   // Restore tok and otok
799         etok = inobj->in_etok;
800
801         switch (inobj->in_type)
802         {
803         case SRC_IFILE:                 // Pop and release an IFILE
804         {
805                 DEBUG { printf("[Leaving: %s]\n", curfname); }
806
807                 IFILE * ifile = inobj->inobj.ifile;
808                 ifile->if_link = f_ifile;
809                 f_ifile = ifile;
810                 close(ifile->ifhandle);                 // Close source file
811 DEBUG { printf("[fpop (pre):  curfname=%s]\n", curfname); }
812                 curfname = ifile->ifoldfname;   // Set current filename
813 DEBUG { printf("[fpop (post): curfname=%s]\n", curfname); }
814 DEBUG { printf("[fpop: (pre)  cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno); }
815                 curlineno = ifile->ifoldlineno; // Set current line#
816                 DEBUG { printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno); }
817                 cfileno = ifile->ifno;                  // Restore current file number
818 DEBUG { printf("[fpop: (post) cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno); }
819                 break;
820         }
821
822         case SRC_IMACRO:                                        // Pop and release an IMACRO
823         {
824                 IMACRO * imacro = inobj->inobj.imacro;
825                 imacro->im_link = f_imacro;
826                 f_imacro = imacro;
827                 break;
828         }
829
830         case SRC_IREPT:                                         // Pop and release an IREPT
831         {
832                 DEBUG { printf("dealloc IREPT\n"); }
833 //              LONG * p = inobj->inobj.irept->ir_firstln;
834                 LLIST * p = inobj->inobj.irept->ir_firstln;
835
836                 // Deallocate repeat lines
837                 while (p != NULL)
838                 {
839 // Shamus: ggn confirmed that this will cause a segfault on 64-bit versions of
840 //         RMAC. This is just stupid and wrong anyway, so we need to fix crapola
841 //         like this...
842 //                      LONG * p1 = (LONG *)*p;
843 //                      p = p1;
844                         free(p->line);
845                         p = p->next;
846                 }
847
848                 break;
849         }
850         }
851
852         cur_inobj = inobj->in_link;
853         inobj->in_link = f_inobj;
854         f_inobj = inobj;
855
856         return 0;
857 }
858
859
860 //
861 // Get line from file into buf, return NULL on EOF or ptr to the start of a
862 // null-term line
863 //
864 char * GetNextLine(void)
865 {
866         int i, j;
867         char * p, * d;
868         int readamt = -1;                                               // 0 if last read() yeilded 0 bytes
869         IFILE * fl = cur_inobj->inobj.ifile;
870
871         for(;;)
872         {
873                 // Scan for next end-of-line; handle stupid text formats by treating
874                 // \r\n the same as \n. (lone '\r' at end of buffer means we have to
875                 // check for '\n').
876                 d = &fl->ifbuf[fl->ifind];
877
878                 for(p=d, i=0, j=fl->ifcnt; i<j; i++, p++)
879                 {
880                         if (*p == '\r' || *p == '\n')
881                         {
882                                 i++;
883
884                                 if (*p == '\r')
885                                 {
886                                         if (i >= j)
887                                                 break;  // Need to read more, then look for '\n' to eat
888                                         else if (p[1] == '\n')
889                                                 i++;
890                                 }
891
892                                 // Cover up the newline with end-of-string sentinel
893                                 *p = '\0';
894
895                                 fl->ifind += i;
896                                 fl->ifcnt -= i;
897                                 return d;
898                         }
899                 }
900
901                 // Handle hanging lines by ignoring them (Input file is exhausted, no
902                 // \r or \n on last line)
903                 // Shamus: This is retarded. Never ignore any input!
904                 if (!readamt && fl->ifcnt)
905                 {
906 #if 0
907                         fl->ifcnt = 0;
908                         *p = '\0';
909                         return NULL;
910 #else
911                         // Really should check to see if we're at the end of the buffer!
912                         // :-P
913                         fl->ifbuf[fl->ifind + fl->ifcnt] = '\0';
914                         fl->ifcnt = 0;
915                         return &fl->ifbuf[fl->ifind];
916 #endif
917                 }
918
919                 // Truncate and return absurdly long lines.
920                 if (fl->ifcnt >= QUANTUM)
921                 {
922                         fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0';
923                         fl->ifcnt = 0;
924                         return &fl->ifbuf[fl->ifind];
925                 }
926
927                 // Relocate what's left of a line to the beginning of the buffer, and
928                 // read some more of the file in; return NULL if the buffer's empty and
929                 // on EOF.
930                 if (fl->ifind != 0)
931                 {
932                         p = &fl->ifbuf[fl->ifind];
933                         d = &fl->ifbuf[fl->ifcnt & 1];
934
935                         for(i=0; i<fl->ifcnt; i++)
936                                 *d++ = *p++;
937
938                         fl->ifind = fl->ifcnt & 1;
939                 }
940
941                 readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM);
942
943                 if (readamt < 0)
944                         return NULL;
945
946                 if ((fl->ifcnt += readamt) == 0)
947                         return NULL;
948         }
949 }
950
951
952 //
953 // Tokenize a line
954 //
955 int TokenizeLine(void)
956 {
957         uint8_t * ln = NULL;            // Ptr to current position in line
958         uint8_t * p;                            // Random character ptr
959         TOKEN * tk;                                     // Token-deposit ptr
960         int state = 0;                          // State for keyword detector
961         int j = 0;                                      // Var for keyword detector
962         uint8_t c;                                      // Random char
963         VALUE v;                                        // Random value
964         uint8_t * nullspot = NULL;      // Spot to clobber for SYMBOL termination
965         int stuffnull;                          // 1:terminate SYMBOL '\0' at *nullspot
966         uint8_t c1;
967         int stringNum = 0;                      // Pointer to string locations in tokenized line
968
969 retry:
970
971         if (cur_inobj == NULL)                                  // Return EOF if input stack is empty
972                 return TKEOF;
973
974         // Get another line of input from the current input source: a file, a
975         // macro, or a repeat-block
976         switch (cur_inobj->in_type)
977         {
978         // Include-file:
979         // o  handle EOF;
980         // o  bump source line number;
981         // o  tag the listing-line with a space;
982         // o  kludge lines generated by Alcyon C.
983         case SRC_IFILE:
984                 if ((ln = GetNextLine()) == NULL)
985                 {
986 DEBUG { printf("TokenizeLine: Calling fpop() from SRC_IFILE...\n"); }
987                         if (fpop() == 0)                                // Pop input level
988                                 goto retry;                                     // Try for more lines
989                         else
990                         {
991                                 ifent->if_prev = (IFENT *)-1;   //Signal Assemble() that we have reached EOF with unbalanced if/endifs
992                                 return TKEOF;
993                         }
994                 }
995
996                 curlineno++;                                            // Bump line number
997                 lntag = SPACE;
998
999                 if (as68_flag)
1000                 {
1001                         // AS68 compatibility, throw away all lines starting with
1002                         // back-quotes, tildes, or '*'
1003                         // On other lines, turn the first '*' into a semi-colon.
1004                         if (*ln == '`' || *ln == '~' || *ln == '*')
1005                                 *ln = ';';
1006                         else
1007                         {
1008                                 for(p=ln; *p!=EOS; p++)
1009                                 {
1010                                         if (*p == '*')
1011                                         {
1012                                                 *p = ';';
1013                                                 break;
1014                                         }
1015                                 }
1016                         }
1017                 }
1018
1019                 break;
1020
1021         // Macro-block:
1022         // o  Handle end-of-macro;
1023         // o  tag the listing-line with an at (@) sign.
1024         case SRC_IMACRO:
1025                 if ((ln = GetNextMacroLine()) == NULL)
1026                 {
1027                         if (ExitMacro() == 0)   // Exit macro (pop args, do fpop(), etc)
1028                                 goto retry;                     // Try for more lines...
1029                         else
1030                                 return TKEOF;           // Oops, we got a non zero return code, signal EOF
1031                 }
1032
1033                 lntag = '@';
1034                 break;
1035
1036         // Repeat-block:
1037         // o  Handle end-of-repeat-block;
1038         // o  tag the listing-line with a pound (#) sign.
1039         case SRC_IREPT:
1040                 if ((ln = GetNextRepeatLine()) == NULL)
1041                 {
1042 DEBUG { printf("TokenizeLine: Calling fpop() from SRC_IREPT...\n"); }
1043                         fpop();
1044                         goto retry;
1045                 }
1046
1047                 lntag = '#';
1048                 break;
1049         }
1050
1051         // Save text of the line. We only do this during listings and within
1052         // macro-type blocks, since it is expensive to unconditionally copy every
1053         // line.
1054         if (lnsave)
1055                 strcpy(lnbuf, ln);
1056
1057         // General housekeeping
1058         tok = tokeol;                   // Set "tok" to EOL in case of error
1059         tk = etok;                              // Reset token ptr
1060         stuffnull = 0;                  // Don't stuff nulls
1061         totlines++;                             // Bump total #lines assembled
1062
1063         // See if the entire line is a comment. This is a win if the programmer
1064         // puts in lots of comments
1065         if (*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln + 1) == '/')))
1066                 goto goteol;
1067
1068         // Main tokenization loop;
1069         //  o  skip whitespace;
1070         //  o  handle end-of-line;
1071         //  o  handle symbols;
1072         //  o  handle single-character tokens (operators, etc.);
1073         //  o  handle multiple-character tokens (constants, strings, etc.).
1074         for(; *ln!=EOS;)
1075         {
1076                 // Skip whitespace, handle EOL
1077                 while (chrtab[*ln] & WHITE)
1078                         ln++;
1079
1080                 // Handle EOL, comment with ';'
1081                 if (*ln == EOS || *ln == ';'|| ((*ln == '/') && (*(ln + 1) == '/')))
1082                         break;
1083
1084                 // Handle start of symbol. Symbols are null-terminated in place. The
1085                 // termination is always one symbol behind, since there may be no place
1086                 // for a null in the case that an operator immediately follows the name.
1087                 c = chrtab[*ln];
1088
1089                 if (c & STSYM)
1090                 {
1091                         if (stuffnull)                  // Terminate old symbol from previous pass
1092                                 *nullspot = EOS;
1093
1094                         v = 0;                                  // Assume no DOT attrib follows symbol
1095                         stuffnull = 1;
1096
1097                         // In some cases, we need to check for a DOTx at the *beginning*
1098                         // of a symbol, as the "start" of the line we're currently looking
1099                         // at could be somewhere in the middle of that line!
1100                         if (*ln == '.')
1101                         {
1102                                 // Make sure that it's *only* a .[bwsl] following, and not the
1103                                 // start of a local symbol:
1104                                 if ((chrtab[*(ln + 1)] & DOT)
1105                                         && (dotxtab[*(ln + 1)] != 0)
1106                                         && !(chrtab[*(ln + 2)] & CTSYM))
1107                                 {
1108                                         // We found a legitimate DOTx construct, so add it to the
1109                                         // token stream:
1110                                         ln++;
1111                                         stuffnull = 0;
1112                                         *tk++ = (TOKEN)dotxtab[*ln++];
1113                                         continue;
1114                                 }
1115                         }
1116
1117                         p = nullspot = ln++;    // Nullspot -> start of this symbol
1118
1119                         // Find end of symbol (and compute its length)
1120                         for(j=1; (int)chrtab[*ln]&CTSYM; j++)
1121                                 ln++;
1122
1123                         // Handle "DOT" special forms (like ".b") that follow a normal
1124                         // symbol or keyword:
1125                         if (*ln == '.')
1126                         {
1127                                 *ln++ = EOS;            // Terminate symbol
1128                                 stuffnull = 0;          // And never try it again
1129
1130                                 // Character following the `.' must have a DOT attribute, and
1131                                 // the chararacter after THAT one must not have a start-symbol
1132                                 // attribute (to prevent symbols that look like, for example,
1133                                 // "zingo.barf", which might be a good idea anyway....)
1134                                 if (((chrtab[*ln] & DOT) == 0) || (dotxtab[*ln] == 0))
1135                                         return error("[bwsl] must follow '.' in symbol");
1136
1137                                 v = (VALUE)dotxtab[*ln++];
1138
1139                                 if (chrtab[*ln] & CTSYM)
1140                                         return error("misuse of '.'; not allowed in symbols");
1141                         }
1142
1143                         // If the symbol is small, check to see if it's really the name of
1144                         // a register.
1145                         if (j <= KWSIZE)
1146                         {
1147                                 for(state=0; state>=0;)
1148                                 {
1149                                         j = (int)tolowertab[*p++];
1150                                         j += kwbase[state];
1151
1152                                         if (kwcheck[j] != state)
1153                                         {
1154                                                 j = -1;
1155                                                 break;
1156                                         }
1157
1158                                         if (*p == EOS || p == ln)
1159                                         {
1160                                                 j = kwaccept[j];
1161                                                 break;
1162                                         }
1163
1164                                         state = kwtab[j];
1165                                 }
1166                         }
1167                         else
1168                         {
1169                                 j = -1;
1170                         }
1171
1172                         // Make j = -1 if user tries to use a RISC register while in 68K mode
1173                         if (!(rgpu || rdsp) && ((TOKEN)j >= KW_R0 && (TOKEN)j <= KW_R31))
1174                         {
1175                                 j = -1;
1176                         }
1177
1178                         // Make j = -1 if time, date etc with no preceeding ^^
1179                         // defined, referenced, streq, macdef, date and time
1180                         switch ((TOKEN)j)
1181                         {
1182                         case 112:   // defined
1183                         case 113:   // referenced
1184                         case 118:   // streq
1185                         case 119:   // macdef
1186                         case 120:   // time
1187                         case 121:   // date
1188                                 j = -1;
1189                         }
1190
1191                         // If not tokenized keyword OR token was not found
1192                         if ((j < 0) || (state < 0))
1193                         {
1194                                 *tk++ = SYMBOL;
1195 //#warning
1196 //problem here: nullspot is a char * but TOKEN is a uint32_t. On a 64-bit
1197 //system, this will cause all kinds of mischief.
1198 #if 0
1199                                 *tk++ = (TOKEN)nullspot;
1200 #else
1201                                 string[stringNum] = nullspot;
1202                                 *tk++ = stringNum;
1203                                 stringNum++;
1204 #endif
1205                         }
1206                         else
1207                         {
1208                                 *tk++ = (TOKEN)j;
1209                                 stuffnull = 0;
1210                         }
1211
1212                         if (v)                                                  // Record attribute token (if any)
1213                                 *tk++ = (TOKEN)v;
1214
1215                         if (stuffnull)                                  // Arrange for string termination on next pass
1216                                 nullspot = ln;
1217
1218                         continue;
1219                 }
1220
1221                 // Handle identity tokens
1222                 if (c & SELF)
1223                 {
1224                         *tk++ = *ln++;
1225                         continue;
1226                 }
1227
1228                 // Handle multiple-character tokens
1229                 if (c & MULTX)
1230                 {
1231                         switch (*ln++)
1232                         {
1233                         case '!':               // ! or !=
1234                                 if (*ln == '=')
1235                                 {
1236                                         *tk++ = NE;
1237                                         ++ln;
1238                                 }
1239                                 else
1240                                         *tk++ = '!';
1241
1242                                 continue;
1243                         case '\'':              // 'string'
1244                                 if (m6502)
1245                                 {
1246                                         // Hardcoded for now, maybe this will change in the future
1247                                         *tk++ = STRINGA8;
1248                                         goto dostring;
1249                                 }
1250                                 // Fall through
1251                         case '\"':              // "string"
1252                                 *tk++ = STRING;
1253 dostring:
1254                                 c1 = ln[-1];
1255                                 string[stringNum] = ln;
1256                                 *tk++ = stringNum;
1257                                 stringNum++;
1258
1259                                 for(p=ln; *ln!=EOS && *ln!=c1;)
1260                                 {
1261                                         c = *ln++;
1262
1263                                         if (c == '\\')
1264                                         {
1265                                                 switch (*ln++)
1266                                                 {
1267                                                 case EOS:
1268                                                         return(error("unterminated string"));
1269                                                 case 'e':
1270                                                         c = '\033';
1271                                                         break;
1272                                                 case 'n':
1273                                                         c = '\n';
1274                                                         break;
1275                                                 case 'b':
1276                                                         c = '\b';
1277                                                         break;
1278                                                 case 't':
1279                                                         c = '\t';
1280                                                         break;
1281                                                 case 'r':
1282                                                         c = '\r';
1283                                                         break;
1284                                                 case 'f':
1285                                                         c = '\f';
1286                                                         break;
1287                                                 case '\"':
1288                                                         c = '\"';
1289                                                         break;
1290                                                 case '\'':
1291                                                         c = '\'';
1292                                                         break;
1293                                                 case '\\':
1294                                                         c = '\\';
1295                                                         break;
1296                                                 default:
1297                                                         warn("bad backslash code in string");
1298                                                         ln--;
1299                                                         break;
1300                                                 }
1301                                         }
1302
1303                                         *p++ = c;
1304                                 }
1305
1306                                 if (*ln++ != c1)
1307                                         return error("unterminated string");
1308
1309                                 *p++ = EOS;
1310                                 continue;
1311                         case '$':               // $, hex constant
1312                                 if (chrtab[*ln] & HDIGIT)
1313                                 {
1314                                         v = 0;
1315
1316                                         // Parse the hex value
1317                                         while (hextab[*ln] >= 0)
1318                                                 v = (v << 4) + (int)hextab[*ln++];
1319
1320                                         if (*ln == '.')
1321                                         {
1322                                                 if (obj_format == BSD)
1323                                                 {
1324                                                         if ((*(ln + 1) & 0xDF) == 'B')
1325                                                         {
1326                                                                 v &= 0x000000FF;
1327                                                                 ln += 2;
1328                                                         }
1329                                                         else if ((*(ln + 1) & 0xDF) == 'W')
1330                                                         {
1331                                                                 v &= 0x0000FFFF;
1332                                                                 ln += 2;
1333                                                         }
1334                                                         else if ((*(ln + 1) & 0xDF) == 'L')
1335                                                         {
1336                                                                 ln += 2;
1337                                                         }
1338                                                 }
1339                                         }
1340
1341                                         *tk++ = CONST;
1342                                         *tk++ = v;
1343
1344                                         if (obj_format == ALCYON)
1345                                         {
1346                                                 if (*ln == '.')
1347                                                 {
1348                                                         if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1349                                                         {
1350                                                                 *tk++ = DOTW;
1351                                                                 ln += 2;
1352                                                         }
1353                                                         else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1354                                                         {
1355                                                                 *tk++ = DOTL;
1356                                                                 ln += 2;
1357                                                         }
1358                                                 }
1359                                         }
1360                                 }
1361                                 else
1362                                         *tk++ = '$';
1363
1364                                 continue;
1365                         case '<':               // < or << or <> or <=
1366                                 switch (*ln)
1367                                 {
1368                                 case '<':
1369                                         *tk++ = SHL;
1370                                         ++ln;
1371                                         continue;
1372                                 case '>':
1373                                         *tk++ = NE;
1374                                         ++ln;
1375                                         continue;
1376                                 case '=':
1377                                         *tk++ = LE;
1378                                         ++ln;
1379                                         continue;
1380                                 default:
1381                                         *tk++ = '<';
1382                                         continue;
1383                                 }
1384                         case ':':               // : or ::
1385                                 if (*ln == ':')
1386                                 {
1387                                         *tk++ = DCOLON;
1388                                         ++ln;
1389                                 }
1390                                 else
1391                                         *tk++ = ':';
1392
1393                                 continue;
1394                         case '=':               // = or ==
1395                                 if (*ln == '=')
1396                                 {
1397                                         *tk++ = DEQUALS;
1398                                         ++ln;
1399                                 }
1400                                 else
1401                                         *tk++ = '=';
1402
1403                                 continue;
1404                         case '>':               // > or >> or >=
1405                                 switch (*ln)
1406                                 {
1407                                 case '>':
1408                                         *tk++ = SHR;
1409                                         ln++;
1410                                         continue;
1411                                 case '=':
1412                                         *tk++ = GE;
1413                                         ln++;
1414                                         continue;
1415                                 default:
1416                                         *tk++ = '>';
1417                                         continue;
1418                                 }
1419                         case '%':               // % or binary constant
1420                                 if (*ln < '0' || *ln > '1')
1421                                 {
1422                                         *tk++ = '%';
1423                                         continue;
1424                                 }
1425
1426                                 v = 0;
1427
1428                                 while (*ln >= '0' && *ln <= '1')
1429                                         v = (v << 1) + *ln++ - '0';
1430
1431                                 if (*ln == '.')
1432                                 {
1433                                         if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1434                                         {
1435                                                 v &= 0x000000FF;
1436                                                 ln += 2;
1437                                         }
1438
1439                                         if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1440                                         {
1441                                                 v &= 0x0000FFFF;
1442                                                 ln += 2;
1443                                         }
1444
1445                                         if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1446                                         {
1447                                                 ln += 2;
1448                                         }
1449                                 }
1450
1451                                 *tk++ = CONST;
1452                                 *tk++ = v;
1453                                 continue;
1454                         case '@':               // @ or octal constant
1455                                 if (*ln < '0' || *ln > '7')
1456                                 {
1457                                         *tk++ = '@';
1458                                         continue;
1459                                 }
1460
1461                                 v = 0;
1462
1463                                 while (*ln >= '0' && *ln <= '7')
1464                                         v = (v << 3) + *ln++ - '0';
1465
1466                                 if (*ln == '.')
1467                                 {
1468                                         if ((*(ln+1) == 'b') || (*(ln+1) == 'B'))
1469                                         {
1470                                                 v &= 0x000000FF;
1471                                                 ln += 2;
1472                                         }
1473
1474                                         if ((*(ln+1) == 'w') || (*(ln+1) == 'W'))
1475                                         {
1476                                                 v &= 0x0000FFFF;
1477                                                 ln += 2;
1478                                         }
1479
1480                                         if ((*(ln+1) == 'l') || (*(ln+1) == 'L'))
1481                                         {
1482                                                 ln += 2;
1483                                         }
1484                                 }
1485
1486                                 *tk++ = CONST;
1487                                 *tk++ = v;
1488                                 continue;
1489                         case '^':               // ^ or ^^ <operator-name>
1490                                 if (*ln != '^')
1491                                 {
1492                                         *tk++ = '^';
1493                                         continue;
1494                                 }
1495
1496                                 if (((int)chrtab[*++ln] & STSYM) == 0)
1497                                 {
1498                                         error("invalid symbol following ^^");
1499                                         continue;
1500                                 }
1501
1502                                 p = ln++;
1503
1504                                 while ((int)chrtab[*ln] & CTSYM)
1505                                         ++ln;
1506
1507                                 for(state=0; state>=0;)
1508                                 {
1509                                         // Get char, convert to lowercase
1510                                         j = *p++;
1511
1512                                         if (j >= 'A' && j <= 'Z')
1513                                                 j += 0x20;
1514
1515                                         j += kwbase[state];
1516
1517                                         if (kwcheck[j] != state)
1518                                         {
1519                                                 j = -1;
1520                                                 break;
1521                                         }
1522
1523                                         if (*p == EOS || p == ln)
1524                                         {
1525                                                 j = kwaccept[j];
1526                                                 break;
1527                                         }
1528
1529                                         state = kwtab[j];
1530                                 }
1531
1532                                 if (j < 0 || state < 0)
1533                                 {
1534                                         error("unknown symbol following ^^");
1535                                         continue;
1536                                 }
1537
1538                                 *tk++ = (TOKEN)j;
1539                                 continue;
1540                         default:
1541                                 interror(2);    // Bad MULTX entry in chrtab
1542                                 continue;
1543                         }
1544                 }
1545
1546                 // Handle decimal constant
1547                 if (c & DIGIT)
1548                 {
1549                         v = 0;
1550
1551                         while ((int)chrtab[*ln] & DIGIT)
1552                                 v = (v * 10) + *ln++ - '0';
1553
1554                         // See if there's a .[bwl] after the constant & deal with it if so
1555                         if (*ln == '.')
1556                         {
1557                                 if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1558                                 {
1559                                         v &= 0x000000FF;
1560                                         ln += 2;
1561                                 }
1562                                 else if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1563                                 {
1564                                         v &= 0x0000FFFF;
1565                                         ln += 2;
1566                                 }
1567                                 else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1568                                 {
1569                                         ln += 2;
1570                                 }
1571                         }
1572
1573                         *tk++ = CONST;
1574                         *tk++ = v;
1575 //printf("CONST: %i\n", v);
1576                         continue;
1577                 }
1578
1579                 // Handle illegal character
1580                 return error("illegal character $%02X found", *ln);
1581         }
1582
1583         // Terminate line of tokens and return "success."
1584
1585 goteol:
1586         tok = etok;                                                             // Set tok to beginning of line
1587
1588         if (stuffnull)                                                  // Terminate last SYMBOL
1589                 *nullspot = EOS;
1590
1591         *tk++ = EOL;
1592
1593         return OK;
1594 }
1595
1596
1597 //
1598 // .GOTO <label>        goto directive
1599 //
1600 // The label is searched for starting from the first line of the current,
1601 // enclosing macro definition. If no enclosing macro exists, an error is
1602 // generated.
1603 //
1604 // A label is of the form:
1605 //
1606 // :<name><whitespace>
1607 //
1608 // The colon must appear in column 1.  The label is stripped prior to macro
1609 // expansion, and is NOT subject to macro expansion.  The whitespace may also
1610 // be EOL.
1611 //
1612 int d_goto(WORD unused)
1613 {
1614         // Setup for the search
1615         if (*tok != SYMBOL)
1616                 return error("missing label");
1617
1618         char * sym = string[tok[1]];
1619         tok += 2;
1620
1621         if (cur_inobj->in_type != SRC_IMACRO)
1622                 return error("goto not in macro");
1623
1624         IMACRO * imacro = cur_inobj->inobj.imacro;
1625         LLIST * defln = imacro->im_macro->lineList;
1626
1627         // Attempt to find the label, starting with the first line.
1628         for(; defln!=NULL; defln=defln->next)
1629         {
1630                 // Must start with a colon
1631                 if (defln->line[0] == ':')
1632                 {
1633                         // Compare names (sleazo string compare)
1634                         char * s1 = sym;
1635                         char * s2 = defln->line;
1636
1637                         // Either we will match the strings to EOS on both, or we will
1638                         // match EOS on string 1 to whitespace on string 2. Otherwise, we
1639                         // have no match.
1640                         while ((*s1 == *s2) || ((*s1 == EOS) && (chrtab[*s2] & WHITE)))
1641                         {
1642                                 // If we reached the end of string 1 (sym), we're done.
1643                                 // Note that we're also checking for the end of string 2 as
1644                                 // well, since we've established they're equal above.
1645                                 if (*s1 == EOS)
1646                                 {
1647                                         // Found the label, set new macro next-line and return.
1648                                         imacro->im_nextln = defln;
1649                                         return 0;
1650                                 }
1651
1652                                 s1++;
1653                                 s2++;
1654                         }
1655                 }
1656         }
1657
1658         return error("goto label not found");
1659 }
1660
1661
1662 void DumpTokenBuffer(void)
1663 {
1664         TOKEN * t;
1665         printf("Tokens [%X]: ", sloc);
1666
1667         for(t=tokbuf; *t!=EOL; t++)
1668         {
1669                 if (*t == COLON)
1670                         printf("[COLON]");
1671                 else if (*t == CONST)
1672                 {
1673                         t++;
1674                         printf("[CONST: $%X]", (uint32_t)*t);
1675                 }
1676                 else if (*t == ACONST)
1677                         printf("[ACONST]");
1678                 else if (*t == STRING)
1679                 {
1680                         t++;
1681                         printf("[STRING:\"%s\"]", string[*t]);
1682                 }
1683                 else if (*t == SYMBOL)
1684                 {
1685                         t++;
1686                         printf("[SYMBOL:\"%s\"]", string[*t]);
1687                 }
1688                 else if (*t == EOS)
1689                         printf("[EOS]");
1690                 else if (*t == TKEOF)
1691                         printf("[TKEOF]");
1692                 else if (*t == DEQUALS)
1693                         printf("[DEQUALS]");
1694                 else if (*t == SET)
1695                         printf("[SET]");
1696                 else if (*t == REG)
1697                         printf("[REG]");
1698                 else if (*t == DCOLON)
1699                         printf("[DCOLON]");
1700                 else if (*t == GE)
1701                         printf("[GE]");
1702                 else if (*t == LE)
1703                         printf("[LE]");
1704                 else if (*t == NE)
1705                         printf("[NE]");
1706                 else if (*t == SHR)
1707                         printf("[SHR]");
1708                 else if (*t == SHL)
1709                         printf("[SHL]");
1710                 else if (*t == UNMINUS)
1711                         printf("[UNMINUS]");
1712                 else if (*t == DOTB)
1713                         printf("[DOTB]");
1714                 else if (*t == DOTW)
1715                         printf("[DOTW]");
1716                 else if (*t == DOTL)
1717                         printf("[DOTL]");
1718                 else if (*t == DOTI)
1719                         printf("[DOTI]");
1720                 else if (*t == ENDEXPR)
1721                         printf("[ENDEXPR]");
1722                 else if (*t == CR_ABSCOUNT)
1723                         printf("[CR_ABSCOUNT]");
1724                 else if (*t == CR_DEFINED)
1725                         printf("[CR_DEFINED]");
1726                 else if (*t == CR_REFERENCED)
1727                         printf("[CR_REFERENCED]");
1728                 else if (*t == CR_STREQ)
1729                         printf("[CR_STREQ]");
1730                 else if (*t == CR_MACDEF)
1731                         printf("[CR_MACDEF]");
1732                 else if (*t == CR_TIME)
1733                         printf("[CR_TIME]");
1734                 else if (*t == CR_DATE)
1735                         printf("[CR_DATE]");
1736                 else if (*t >= 0x20 && *t <= 0x2F)
1737                         printf("[%c]", (char)*t);
1738                 else if (*t >= 0x3A && *t <= 0x3F)
1739                         printf("[%c]", (char)*t);
1740                 else if (*t >= 0x80 && *t <= 0x87)
1741                         printf("[D%u]", ((uint32_t)*t) - 0x80);
1742                 else if (*t >= 0x88 && *t <= 0x8F)
1743                         printf("[A%u]", ((uint32_t)*t) - 0x88);
1744                 else
1745                         printf("[%X:%c]", (uint32_t)*t, (char)*t);
1746         }
1747
1748         printf("[EOL]\n");
1749 }
1750