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