Long forgotten helper script which is used to create tables for VS builds
[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-2019 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 uint16_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_DATE:
608                                                                 d = "^^date";
609                                                                 break;
610                                                         case CR_TIME:
611                                                                 d = "^^time";
612                                                                 break;
613                                                         case CR_DEFINED:
614                                                                 d = "^^defined ";
615                                                                 break;
616                                                         case CR_REFERENCED:
617                                                                 d = "^^referenced ";
618                                                                 break;
619                                                         case CR_STREQ:
620                                                                 d = "^^streq ";
621                                                                 break;
622                                                         case CR_MACDEF:
623                                                                 d = "^^macdef ";
624                                                                 break;
625                                                         default:
626                                                                 if (dst >= edst)
627                                                                         goto overflow;
628
629                                                                 *dst++ = (char)*(tk - 1);
630                                                                 break;
631                                                         }
632                                                 }
633
634                                                 // If 'd' != NULL, copy string to destination
635                                                 if (d != NULL)
636                                                 {
637 strcopy:
638                                                         DEBUG printf("d='%s'\n", d);
639
640                                                         while (*d != EOS)
641                                                         {
642                                                                 if (dst >= edst)
643                                                                         goto overflow;
644                                                                 else
645                                                                         *dst++ = *d++;
646                                                         }
647                                                 }
648                                         }
649                                 }
650                         }
651                 }
652         }
653
654 skipcomments:
655
656         *dst = EOS;
657         DEBUG { printf("ExM: dst=\"%s\"\n", dest); }
658         return OK;
659
660 overflow:
661         *dst = EOS;
662         DEBUG { printf("*** OVERFLOW LINE ***\n%s\n", dest); }
663         return fatal("line too long as a result of macro expansion");
664 }
665
666
667 //
668 // Get next line of text from a macro
669 //
670 char * GetNextMacroLine(void)
671 {
672         IMACRO * imacro = cur_inobj->inobj.imacro;
673 //      LONG * strp = imacro->im_nextln;
674         LLIST * strp = imacro->im_nextln;
675
676         if (strp == NULL)                                               // End-of-macro
677                 return NULL;
678
679         imacro->im_nextln = strp->next;
680 //      ExpandMacro((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
681         ExpandMacro(strp->line, imacro->im_lnbuf, LNSIZ);
682
683         return imacro->im_lnbuf;
684 }
685
686
687 //
688 // Get next line of text from a repeat block
689 //
690 char * GetNextRepeatLine(void)
691 {
692         IREPT * irept = cur_inobj->inobj.irept;
693 //      LONG * strp = irept->ir_nextln;                 // initial null
694
695         // Do repeat at end of .rept block's string list
696 //      if (strp == NULL)
697         if (irept->ir_nextln == NULL)
698         {
699                 DEBUG { printf("back-to-top-of-repeat-block count=%d\n", (int)irept->ir_count); }
700                 irept->ir_nextln = irept->ir_firstln;   // copy first line
701
702                 if (irept->ir_count-- == 0)
703                 {
704                         DEBUG { printf("end-repeat-block\n"); }
705                         return NULL;
706                 }
707
708 //              strp = irept->ir_nextln;
709         }
710         // Mark the current macro line in the irept object
711         // This is probably overkill - a global variable
712         // would suffice here (it only gets used during
713         // error reporting anyway)
714         irept->lineno = irept->ir_nextln->lineno;
715
716 //      strcpy(irbuf, (char *)(irept->ir_nextln + 1));
717         strcpy(irbuf, irept->ir_nextln->line);
718         DEBUG { printf("repeat line='%s'\n", irbuf); }
719 //      irept->ir_nextln = (LONG *)*strp;
720         irept->ir_nextln = irept->ir_nextln->next;
721
722         return irbuf;
723 }
724
725
726 //
727 // Include a source file used at the root, and for ".include" files
728 //
729 int include(int handle, char * fname)
730 {
731         // Debug mode
732         DEBUG { printf("[include: %s, cfileno=%u]\n", fname, cfileno); }
733
734         // Alloc and initialize include-descriptors
735         INOBJ * inobj = a_inobj(SRC_IFILE);
736         IFILE * ifile = inobj->inobj.ifile;
737
738         ifile->ifhandle = handle;                       // Setup file handle
739         ifile->ifind = ifile->ifcnt = 0;        // Setup buffer indices
740         ifile->ifoldlineno = curlineno;         // Save old line number
741         ifile->ifoldfname = curfname;           // Save old filename
742         ifile->ifno = cfileno;                          // Save old file number
743
744         // NB: This *must* be preincrement, we're adding one to the filecount here!
745         cfileno = ++filecount;                          // Compute NEW file number
746         curfname = strdup(fname);                       // Set current filename (alloc storage)
747         curlineno = 0;                                          // Start on line zero
748
749         // Add another file to the file-record
750         FILEREC * fr = (FILEREC *)malloc(sizeof(FILEREC));
751         fr->frec_next = NULL;
752         fr->frec_name = curfname;
753
754         if (last_fr == NULL)
755                 filerec = fr;                                   // Add first filerec
756         else
757                 last_fr->frec_next = fr;                // Append to list of filerecs
758
759         last_fr = fr;
760         DEBUG { printf("[include: curfname: %s, cfileno=%u]\n", curfname, cfileno); }
761
762         return OK;
763 }
764
765
766 //
767 // Pop the current input level
768 //
769 int fpop(void)
770 {
771         INOBJ * inobj = cur_inobj;
772
773         if (inobj == NULL)
774                 return 0;
775
776         // Pop IFENT levels until we reach the conditional assembly context we
777         // were at when the input object was entered.
778         int numUnmatched = 0;
779
780         while (ifent != inobj->in_ifent)
781         {
782                 if (d_endif() != 0)     // Something bad happened during endif parsing?
783                         return -1;              // If yes, bail instead of getting stuck in a loop
784
785                 numUnmatched++;
786         }
787
788         // Give a warning to the user that we had to wipe their bum for them
789         if (numUnmatched > 0)
790                 warn("missing %d .endif(s)", numUnmatched);
791
792         tok = inobj->in_otok;   // Restore tok and otok
793         etok = inobj->in_etok;
794
795         switch (inobj->in_type)
796         {
797         case SRC_IFILE:                 // Pop and release an IFILE
798         {
799                 DEBUG { printf("[Leaving: %s]\n", curfname); }
800
801                 IFILE * ifile = inobj->inobj.ifile;
802                 ifile->if_link = f_ifile;
803                 f_ifile = ifile;
804                 close(ifile->ifhandle);                 // Close source file
805 DEBUG { printf("[fpop (pre):  curfname=%s]\n", curfname); }
806                 curfname = ifile->ifoldfname;   // Set current filename
807 DEBUG { printf("[fpop (post): curfname=%s]\n", curfname); }
808 DEBUG { printf("[fpop: (pre)  cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno); }
809                 curlineno = ifile->ifoldlineno; // Set current line#
810                 DEBUG { printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno); }
811                 cfileno = ifile->ifno;                  // Restore current file number
812 DEBUG { printf("[fpop: (post) cfileno=%d ifile->ifno=%d]\n", (int)cfileno, (int)ifile->ifno); }
813                 break;
814         }
815
816         case SRC_IMACRO:                                        // Pop and release an IMACRO
817         {
818                 IMACRO * imacro = inobj->inobj.imacro;
819                 imacro->im_link = f_imacro;
820                 f_imacro = imacro;
821                 break;
822         }
823
824         case SRC_IREPT:                                         // Pop and release an IREPT
825         {
826                 DEBUG { printf("dealloc IREPT\n"); }
827                 LLIST * p = inobj->inobj.irept->ir_firstln;
828
829                 // Deallocate repeat lines
830                 while (p != NULL)
831                 {
832                         free(p->line);
833                         p = p->next;
834                 }
835
836                 break;
837         }
838         }
839
840         cur_inobj = inobj->in_link;
841         inobj->in_link = f_inobj;
842         f_inobj = inobj;
843
844         return 0;
845 }
846
847
848 //
849 // Get line from file into buf, return NULL on EOF or ptr to the start of a
850 // null-term line
851 //
852 char * GetNextLine(void)
853 {
854         int i, j;
855         char * p, * d;
856         int readamt = -1;                                               // 0 if last read() yeilded 0 bytes
857         IFILE * fl = cur_inobj->inobj.ifile;
858
859         for(;;)
860         {
861                 // Scan for next end-of-line; handle stupid text formats by treating
862                 // \r\n the same as \n. (lone '\r' at end of buffer means we have to
863                 // check for '\n').
864                 d = &fl->ifbuf[fl->ifind];
865
866                 for(p=d, i=0, j=fl->ifcnt; i<j; i++, p++)
867                 {
868                         if (*p == '\r' || *p == '\n')
869                         {
870                                 i++;
871
872                                 if (*p == '\r')
873                                 {
874                                         if (i >= j)
875                                                 break;  // Need to read more, then look for '\n' to eat
876                                         else if (p[1] == '\n')
877                                                 i++;
878                                 }
879
880                                 // Cover up the newline with end-of-string sentinel
881                                 *p = '\0';
882
883                                 fl->ifind += i;
884                                 fl->ifcnt -= i;
885                                 return d;
886                         }
887                 }
888
889                 // Handle hanging lines by ignoring them (Input file is exhausted, no
890                 // \r or \n on last line)
891                 // Shamus: This is retarded. Never ignore any input!
892                 if (!readamt && fl->ifcnt)
893                 {
894 #if 0
895                         fl->ifcnt = 0;
896                         *p = '\0';
897                         return NULL;
898 #else
899                         // Really should check to see if we're at the end of the buffer!
900                         // :-P
901                         fl->ifbuf[fl->ifind + fl->ifcnt] = '\0';
902                         fl->ifcnt = 0;
903                         return &fl->ifbuf[fl->ifind];
904 #endif
905                 }
906
907                 // Truncate and return absurdly long lines.
908                 if (fl->ifcnt >= QUANTUM)
909                 {
910                         fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0';
911                         fl->ifcnt = 0;
912                         return &fl->ifbuf[fl->ifind];
913                 }
914
915                 // Relocate what's left of a line to the beginning of the buffer, and
916                 // read some more of the file in; return NULL if the buffer's empty and
917                 // on EOF.
918                 if (fl->ifind != 0)
919                 {
920                         p = &fl->ifbuf[fl->ifind];
921                         d = &fl->ifbuf[fl->ifcnt & 1];
922
923                         for(i=0; i<fl->ifcnt; i++)
924                                 *d++ = *p++;
925
926                         fl->ifind = fl->ifcnt & 1;
927                 }
928
929                 readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM);
930
931                 if (readamt < 0)
932                         return NULL;
933
934                 if ((fl->ifcnt += readamt) == 0)
935                         return NULL;
936         }
937 }
938
939
940 //
941 // Tokenize a line
942 //
943 int TokenizeLine(void)
944 {
945         uint8_t * ln = NULL;            // Ptr to current position in line
946         uint8_t * p;                            // Random character ptr
947         PTR tk;                                         // Token-deposit ptr
948         int state = 0;                          // State for keyword detector
949         int j = 0;                                      // Var for keyword detector
950         uint8_t c;                                      // Random char
951         uint64_t v;                                     // Random value
952         uint32_t cursize = 0;           // Current line's size (.b, .w, .l, .s, .q, .d)
953         double f;                                       // Random float
954         uint8_t * nullspot = NULL;      // Spot to clobber for SYMBOL termination
955         int stuffnull;                          // 1:terminate SYMBOL '\0' at *nullspot
956         uint8_t c1;
957         int stringNum = 0;                      // Pointer to string locations in tokenized line
958
959 retry:
960
961         if (cur_inobj == NULL)          // Return EOF if input stack is empty
962                 return TKEOF;
963
964         // Get another line of input from the current input source: a file, a
965         // macro, or a repeat-block
966         switch (cur_inobj->in_type)
967         {
968         // Include-file:
969         // o  handle EOF;
970         // o  bump source line number;
971         // o  tag the listing-line with a space;
972         // o  kludge lines generated by Alcyon C.
973         case SRC_IFILE:
974                 if ((ln = GetNextLine()) == NULL)
975                 {
976 DEBUG { printf("TokenizeLine: Calling fpop() from SRC_IFILE...\n"); }
977                         if (fpop() == 0)        // Pop input level
978                                 goto retry;             // Try for more lines
979                         else
980                         {
981                                 ifent->if_prev = (IFENT *)-1;   //Signal Assemble() that we have reached EOF with unbalanced if/endifs
982                                 return TKEOF;
983                         }
984                 }
985
986                 curlineno++;                    // Bump line number
987                 lntag = SPACE;
988
989                 if (as68_flag)
990                 {
991                         // AS68 compatibility, throw away all lines starting with
992                         // back-quotes, tildes, or '*'
993                         // On other lines, turn the first '*' into a semi-colon.
994                         if (*ln == '`' || *ln == '~' || *ln == '*')
995                                 *ln = ';';
996                         else
997                         {
998                                 for(p=ln; *p!=EOS; p++)
999                                 {
1000                                         if (*p == '*')
1001                                         {
1002                                                 *p = ';';
1003                                                 break;
1004                                         }
1005                                 }
1006                         }
1007                 }
1008
1009                 break;
1010
1011         // Macro-block:
1012         // o  Handle end-of-macro;
1013         // o  tag the listing-line with an at (@) sign.
1014         case SRC_IMACRO:
1015                 if ((ln = GetNextMacroLine()) == NULL)
1016                 {
1017                         if (ExitMacro() == 0)   // Exit macro (pop args, do fpop(), etc)
1018                                 goto retry;                     // Try for more lines...
1019                         else
1020                                 return TKEOF;           // Oops, we got a non zero return code, signal EOF
1021                 }
1022
1023                 lntag = '@';
1024                 break;
1025
1026         // Repeat-block:
1027         // o  Handle end-of-repeat-block;
1028         // o  tag the listing-line with a pound (#) sign.
1029         case SRC_IREPT:
1030                 if ((ln = GetNextRepeatLine()) == NULL)
1031                 {
1032                         DEBUG { printf("TokenizeLine: Calling fpop() from SRC_IREPT...\n"); }
1033                         fpop();
1034                         goto retry;
1035                 }
1036
1037                 lntag = '#';
1038                 break;
1039         }
1040
1041         // Save text of the line. We only do this during listings and within
1042         // macro-type blocks, since it is expensive to unconditionally copy every
1043         // line.
1044         if (lnsave)
1045                 strcpy(lnbuf, ln);
1046
1047         // General housekeeping
1048         tok = tokeol;                   // Set "tok" to EOL in case of error
1049         tk.u32 = etok;                  // Reset token ptr
1050         stuffnull = 0;                  // Don't stuff nulls
1051         totlines++;                             // Bump total #lines assembled
1052
1053         // See if the entire line is a comment. This is a win if the programmer
1054         // puts in lots of comments
1055         if (*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln + 1) == '/')))
1056                 goto goteol;
1057
1058         // And here we have a very ugly hack for signalling a single line 'turn off
1059         // optimization'. There's really no nice way to do this, so hack it is!
1060         optimizeOff = 0;                // Default is to take optimizations as they come
1061
1062         if (*ln == '!')
1063         {
1064                 optimizeOff = 1;        // Signal that we don't want to optimize this line
1065                 ln++;                           // & skip over the darned thing
1066         }
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.u32++ = (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 = (uint32_t)dotxtab[*ln++];
1138                                 cursize = (uint32_t)v;
1139
1140                                 if (chrtab[*ln] & CTSYM)
1141                                         return error("misuse of '.'; not allowed in symbols");
1142                         }
1143
1144                         // If the symbol is small, check to see if it's really the name of
1145                         // a register.
1146                         if (j <= KWSIZE)
1147                         {
1148                                 for(state=0; state>=0;)
1149                                 {
1150                                         j = (int)tolowertab[*p++];
1151                                         j += kwbase[state];
1152
1153                                         if (kwcheck[j] != state)
1154                                         {
1155                                                 j = -1;
1156                                                 break;
1157                                         }
1158
1159                                         if (*p == EOS || p == ln)
1160                                         {
1161                                                 j = kwaccept[j];
1162                                                 break;
1163                                         }
1164
1165                                         state = kwtab[j];
1166                                 }
1167                         }
1168                         else
1169                         {
1170                                 j = -1;
1171                         }
1172
1173                         // Make j = -1 if user tries to use a RISC register while in 68K mode
1174                         if (!(rgpu || rdsp || dsp56001) && ((TOKEN)j >= KW_R0 && (TOKEN)j <= KW_R31))
1175                         {
1176                                 j = -1;
1177                         }
1178
1179                         // Make j = -1 if time, date etc with no preceeding ^^
1180                         // defined, referenced, streq, macdef, date and time
1181                         switch ((TOKEN)j)
1182                         {
1183                         case 112:   // defined
1184                         case 113:   // referenced
1185                         case 118:   // streq
1186                         case 119:   // macdef
1187                         case 120:   // time
1188                         case 121:   // date
1189                                 j = -1;
1190                         }
1191
1192                         // If not tokenized keyword OR token was not found
1193                         if ((j < 0) || (state < 0))
1194                         {
1195                                 *tk.u32++ = SYMBOL;
1196                                 string[stringNum] = nullspot;
1197                                 *tk.u32++ = stringNum;
1198                                 stringNum++;
1199                         }
1200                         else
1201                         {
1202                                 *tk.u32++ = (TOKEN)j;
1203                                 stuffnull = 0;
1204                         }
1205
1206                         if (v)                  // Record attribute token (if any)
1207                                 *tk.u32++ = (TOKEN)v;
1208
1209                         if (stuffnull)  // Arrange for string termination on next pass
1210                                 nullspot = ln;
1211
1212                         continue;
1213                 }
1214
1215                 // Handle identity tokens
1216                 if (c & SELF)
1217                 {
1218                         *tk.u32++ = *ln++;
1219                         continue;
1220                 }
1221
1222                 // Handle multiple-character tokens
1223                 if (c & MULTX)
1224                 {
1225                         switch (*ln++)
1226                         {
1227                         case '!':               // ! or !=
1228                                 if (*ln == '=')
1229                                 {
1230                                         *tk.u32++ = NE;
1231                                         ln++;
1232                                 }
1233                                 else
1234                                         *tk.u32++ = '!';
1235
1236                                 continue;
1237                         case '\'':              // 'string'
1238                                 if (m6502)
1239                                 {
1240                                         // Hardcoded for now, maybe this will change in the future
1241                                         *tk.u32++ = STRINGA8;
1242                                         goto dostring;
1243                                 }
1244                                 // Fall through
1245                         case '\"':              // "string"
1246                                 *tk.u32++ = STRING;
1247 dostring:
1248                                 c1 = ln[-1];
1249                                 string[stringNum] = ln;
1250                                 *tk.u32++ = stringNum;
1251                                 stringNum++;
1252
1253                                 for(p=ln; *ln!=EOS && *ln!=c1;)
1254                                 {
1255                                         c = *ln++;
1256
1257                                         if (c == '\\')
1258                                         {
1259                                                 switch (*ln++)
1260                                                 {
1261                                                 case EOS:
1262                                                         return(error("unterminated string"));
1263                                                 case 'e':
1264                                                         c = '\033';
1265                                                         break;
1266                                                 case 'n':
1267                                                         c = '\n';
1268                                                         break;
1269                                                 case 'b':
1270                                                         c = '\b';
1271                                                         break;
1272                                                 case 't':
1273                                                         c = '\t';
1274                                                         break;
1275                                                 case 'r':
1276                                                         c = '\r';
1277                                                         break;
1278                                                 case 'f':
1279                                                         c = '\f';
1280                                                         break;
1281                                                 case '\"':
1282                                                         c = '\"';
1283                                                         break;
1284                                                 case '\'':
1285                                                         c = '\'';
1286                                                         break;
1287                                                 case '\\':
1288                                                         c = '\\';
1289                                                         break;
1290                                                 case '{':
1291                                                         // If we're evaluating a macro
1292                                                         // this is valid because it's
1293                                                         // a parameter expansion
1294                                                 case '!':
1295                                                         // If we're evaluating a macro
1296                                                         // this is valid and expands to
1297                                                         // "dot-size"
1298                                                         break;
1299                                                 default:
1300                                                         warn("bad backslash code in string");
1301                                                         ln--;
1302                                                         break;
1303                                                 }
1304                                         }
1305
1306                                         *p++ = c;
1307                                 }
1308
1309                                 if (*ln++ != c1)
1310                                         return error("unterminated string");
1311
1312                                 *p++ = EOS;
1313                                 continue;
1314                         case '$':               // $, hex constant
1315                                 if (chrtab[*ln] & HDIGIT)
1316                                 {
1317                                         v = 0;
1318
1319                                         // Parse the hex value
1320                                         while (hextab[*ln] >= 0)
1321                                                 v = (v << 4) + (int)hextab[*ln++];
1322
1323                                         *tk.u32++ = CONST;
1324                                         *tk.u64++ = v;
1325
1326                                         if (*ln == '.')
1327                                         {
1328                                                 if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1329                                                 {
1330                                                         *tk.u32++ = DOTW;
1331                                                         ln += 2;
1332                                                 }
1333                                                 else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1334                                                 {
1335                                                         *tk.u32++ = DOTL;
1336                                                         ln += 2;
1337                                                 }
1338                                         }
1339                                 }
1340                                 else
1341                                         *tk.u32++ = '$';
1342
1343                                 continue;
1344                         case '<':               // < or << or <> or <=
1345                                 switch (*ln)
1346                                 {
1347                                 case '<':
1348                                         *tk.u32++ = SHL;
1349                                         ln++;
1350                                         continue;
1351                                 case '>':
1352                                         *tk.u32++ = NE;
1353                                         ln++;
1354                                         continue;
1355                                 case '=':
1356                                         *tk.u32++ = LE;
1357                                         ln++;
1358                                         continue;
1359                                 default:
1360                                         *tk.u32++ = '<';
1361                                         continue;
1362                                 }
1363                         case ':':               // : or ::
1364                                 if (*ln == ':')
1365                                 {
1366                                         *tk.u32++ = DCOLON;
1367                                         ln++;
1368                                 }
1369                                 else
1370                                         *tk.u32++ = ':';
1371
1372                                 continue;
1373                         case '=':               // = or ==
1374                                 if (*ln == '=')
1375                                 {
1376                                         *tk.u32++ = DEQUALS;
1377                                         ln++;
1378                                 }
1379                                 else
1380                                         *tk.u32++ = '=';
1381
1382                                 continue;
1383                         case '>':               // > or >> or >=
1384                                 switch (*ln)
1385                                 {
1386                                 case '>':
1387                                         *tk.u32++ = SHR;
1388                                         ln++;
1389                                         continue;
1390                                 case '=':
1391                                         *tk.u32++ = GE;
1392                                         ln++;
1393                                         continue;
1394                                 default:
1395                                         *tk.u32++ = '>';
1396                                         continue;
1397                                 }
1398                         case '%':               // % or binary constant
1399                                 if (*ln < '0' || *ln > '1')
1400                                 {
1401                                         *tk.u32++ = '%';
1402                                         continue;
1403                                 }
1404
1405                                 v = 0;
1406
1407                                 while (*ln >= '0' && *ln <= '1')
1408                                         v = (v << 1) + *ln++ - '0';
1409
1410                                 if (*ln == '.')
1411                                 {
1412                                         if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1413                                         {
1414                                                 v &= 0x000000FF;
1415                                                 ln += 2;
1416                                         }
1417
1418                                         if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1419                                         {
1420                                                 v &= 0x0000FFFF;
1421                                                 ln += 2;
1422                                         }
1423
1424                                         if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1425                                         {
1426                                                 v &= 0xFFFFFFFF;
1427                                                 ln += 2;
1428                                         }
1429                                 }
1430
1431                                 *tk.u32++ = CONST;
1432                                 *tk.u64++ = v;
1433                                 continue;
1434                         case '@':               // @ or octal constant
1435                                 if (*ln < '0' || *ln > '7')
1436                                 {
1437                                         *tk.u32++ = '@';
1438                                         continue;
1439                                 }
1440
1441                                 v = 0;
1442
1443                                 while (*ln >= '0' && *ln <= '7')
1444                                         v = (v << 3) + *ln++ - '0';
1445
1446                                 if (*ln == '.')
1447                                 {
1448                                         if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1449                                         {
1450                                                 v &= 0x000000FF;
1451                                                 ln += 2;
1452                                         }
1453
1454                                         if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1455                                         {
1456                                                 v &= 0x0000FFFF;
1457                                                 ln += 2;
1458                                         }
1459
1460                                         if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1461                                         {
1462                                                 v &= 0xFFFFFFFF;
1463                                                 ln += 2;
1464                                         }
1465                                 }
1466
1467                                 *tk.u32++ = CONST;
1468                                 *tk.u64++ = v;
1469                                 continue;
1470                         case '^':               // ^ or ^^ <operator-name>
1471                                 if (*ln != '^')
1472                                 {
1473                                         *tk.u32++ = '^';
1474                                         continue;
1475                                 }
1476
1477                                 if (((int)chrtab[*++ln] & STSYM) == 0)
1478                                 {
1479                                         error("invalid symbol following ^^");
1480                                         continue;
1481                                 }
1482
1483                                 p = ln++;
1484
1485                                 while ((int)chrtab[*ln] & CTSYM)
1486                                         ++ln;
1487
1488                                 for(state=0; state>=0;)
1489                                 {
1490                                         // Get char, convert to lowercase
1491                                         j = *p++;
1492
1493                                         if (j >= 'A' && j <= 'Z')
1494                                                 j += 0x20;
1495
1496                                         j += kwbase[state];
1497
1498                                         if (kwcheck[j] != state)
1499                                         {
1500                                                 j = -1;
1501                                                 break;
1502                                         }
1503
1504                                         if (*p == EOS || p == ln)
1505                                         {
1506                                                 j = kwaccept[j];
1507                                                 break;
1508                                         }
1509
1510                                         state = kwtab[j];
1511                                 }
1512
1513                                 if (j < 0 || state < 0)
1514                                 {
1515                                         error("unknown symbol following ^^");
1516                                         continue;
1517                                 }
1518
1519                                 *tk.u32++ = (TOKEN)j;
1520                                 continue;
1521                         default:
1522                                 interror(2);    // Bad MULTX entry in chrtab
1523                                 continue;
1524                         }
1525                 }
1526
1527                 // Handle decimal constant
1528                 if (c & DIGIT)
1529                 {
1530                         uint8_t * numStart = ln;
1531                         v = 0;
1532
1533                         while ((int)chrtab[*ln] & DIGIT)
1534                                 v = (v * 10) + *ln++ - '0';
1535
1536                         // See if there's a .[bwl] after the constant & deal with it if so
1537                         if (*ln == '.')
1538                         {
1539                                 if ((*(ln + 1) == 'b') || (*(ln + 1) == 'B'))
1540                                 {
1541                                         v &= 0x000000FF;
1542                                         ln += 2;
1543                                         *tk.u32++ = CONST;
1544                                         *tk.u64++ = v;
1545                                         *tk.u32++ = DOTB;
1546                                 }
1547                                 else if ((*(ln + 1) == 'w') || (*(ln + 1) == 'W'))
1548                                 {
1549                                         v &= 0x0000FFFF;
1550                                         ln += 2;
1551                                         *tk.u32++ = CONST;
1552                                         *tk.u64++ = v;
1553                                         *tk.u32++ = DOTW;
1554                                 }
1555                                 else if ((*(ln + 1) == 'l') || (*(ln + 1) == 'L'))
1556                                 {
1557                                         v &= 0xFFFFFFFF;
1558                                         ln += 2;
1559                                         *tk.u32++ = CONST;
1560                                         *tk.u64++ = v;
1561                                         *tk.u32++ = DOTL;
1562                                 }
1563                                 else if ((int)chrtab[*(ln + 1)] & DIGIT)
1564                                 {
1565                                         // Hey, more digits after the dot, so we assume it's a
1566                                         // floating point number of some kind... numEnd will point
1567                                         // to the first non-float character after it's done
1568                                         char * numEnd;
1569                                         errno = 0;
1570                                         double f = strtod(numStart, &numEnd);
1571                                         ln = (uint8_t *)numEnd;
1572
1573                                         if (errno != 0)
1574                                                 return error("floating point parse error");
1575
1576                                         // N.B.: We use the C compiler's internal double
1577                                         //       representation for all internal float calcs and
1578                                         //       are reasonably sure that the size of said double
1579                                         //       is 8 bytes long (which we check for in fltpoint.c)
1580                                         *tk.u32++ = FCONST;
1581                                         *tk.dp = f;
1582                                         tk.u64++;
1583                                         continue;
1584                                 }
1585                         }
1586                         else
1587                         {
1588                                 *tk.u32++ = CONST;
1589                                 *tk.u64++ = v;
1590                         }
1591
1592 //printf("CONST: %i\n", v);
1593                         continue;
1594                 }
1595
1596                 // Handle illegal character
1597                 return error("illegal character $%02X found", *ln);
1598         }
1599
1600         // Terminate line of tokens and return "success."
1601
1602 goteol:
1603         tok = etok;                             // Set tok to beginning of line
1604
1605         if (stuffnull)                  // Terminate last SYMBOL
1606                 *nullspot = EOS;
1607
1608         *tk.u32++ = EOL;
1609
1610         return OK;
1611 }
1612
1613
1614 //
1615 // .GOTO <label>        goto directive
1616 //
1617 // The label is searched for starting from the first line of the current,
1618 // enclosing macro definition. If no enclosing macro exists, an error is
1619 // generated.
1620 //
1621 // A label is of the form:
1622 //
1623 // :<name><whitespace>
1624 //
1625 // The colon must appear in column 1.  The label is stripped prior to macro
1626 // expansion, and is NOT subject to macro expansion.  The whitespace may also
1627 // be EOL.
1628 //
1629 int d_goto(WORD unused)
1630 {
1631         // Setup for the search
1632         if (*tok != SYMBOL)
1633                 return error("missing label");
1634
1635         char * sym = string[tok[1]];
1636         tok += 2;
1637
1638         if (cur_inobj->in_type != SRC_IMACRO)
1639                 return error("goto not in macro");
1640
1641         IMACRO * imacro = cur_inobj->inobj.imacro;
1642         LLIST * defln = imacro->im_macro->lineList;
1643
1644         // Attempt to find the label, starting with the first line.
1645         for(; defln!=NULL; defln=defln->next)
1646         {
1647                 // Must start with a colon
1648                 if (defln->line[0] == ':')
1649                 {
1650                         // Compare names (sleazo string compare)
1651                         char * s1 = sym;
1652                         char * s2 = defln->line;
1653
1654                         // Either we will match the strings to EOS on both, or we will
1655                         // match EOS on string 1 to whitespace on string 2. Otherwise, we
1656                         // have no match.
1657                         while ((*s1 == *s2) || ((*s1 == EOS) && (chrtab[*s2] & WHITE)))
1658                         {
1659                                 // If we reached the end of string 1 (sym), we're done.
1660                                 // Note that we're also checking for the end of string 2 as
1661                                 // well, since we've established they're equal above.
1662                                 if (*s1 == EOS)
1663                                 {
1664                                         // Found the label, set new macro next-line and return.
1665                                         imacro->im_nextln = defln;
1666                                         return 0;
1667                                 }
1668
1669                                 s1++;
1670                                 s2++;
1671                         }
1672                 }
1673         }
1674
1675         return error("goto label not found");
1676 }
1677
1678
1679 void DumpToken(TOKEN t)
1680 {
1681         if (t == COLON)
1682                 printf("[COLON]");
1683         else if (t == CONST)
1684                 printf("[CONST]");
1685         else if (t == FCONST)
1686                 printf("[FCONST]");
1687         else if (t == ACONST)
1688                 printf("[ACONST]");
1689         else if (t == STRING)
1690                 printf("[STRING]");
1691         else if (t == SYMBOL)
1692                 printf("[SYMBOL]");
1693         else if (t == EOS)
1694                 printf("[EOS]");
1695         else if (t == TKEOF)
1696                 printf("[TKEOF]");
1697         else if (t == DEQUALS)
1698                 printf("[DEQUALS]");
1699         else if (t == SET)
1700                 printf("[SET]");
1701         else if (t == REG)
1702                 printf("[REG]");
1703         else if (t == DCOLON)
1704                 printf("[DCOLON]");
1705         else if (t == GE)
1706                 printf("[GE]");
1707         else if (t == LE)
1708                 printf("[LE]");
1709         else if (t == NE)
1710                 printf("[NE]");
1711         else if (t == SHR)
1712                 printf("[SHR]");
1713         else if (t == SHL)
1714                 printf("[SHL]");
1715         else if (t == UNMINUS)
1716                 printf("[UNMINUS]");
1717         else if (t == DOTB)
1718                 printf("[DOTB]");
1719         else if (t == DOTW)
1720                 printf("[DOTW]");
1721         else if (t == DOTL)
1722                 printf("[DOTL]");
1723         else if (t == DOTQ)
1724                 printf("[DOTQ]");
1725         else if (t == DOTS)
1726                 printf("[DOTS]");
1727         else if (t == DOTD)
1728                 printf("[DOTD]");
1729         else if (t == DOTI)
1730                 printf("[DOTI]");
1731         else if (t == ENDEXPR)
1732                 printf("[ENDEXPR]");
1733         else if (t == CR_ABSCOUNT)
1734                 printf("[CR_ABSCOUNT]");
1735         else if (t == CR_DEFINED)
1736                 printf("[CR_DEFINED]");
1737         else if (t == CR_REFERENCED)
1738                 printf("[CR_REFERENCED]");
1739         else if (t == CR_STREQ)
1740                 printf("[CR_STREQ]");
1741         else if (t == CR_MACDEF)
1742                 printf("[CR_MACDEF]");
1743         else if (t == CR_TIME)
1744                 printf("[CR_TIME]");
1745         else if (t == CR_DATE)
1746                 printf("[CR_DATE]");
1747         else if (t >= 0x20 && t <= 0x2F)
1748                 printf("[%c]", (char)t);
1749         else if (t >= 0x3A && t <= 0x3F)
1750                 printf("[%c]", (char)t);
1751         else if (t >= 0x80 && t <= 0x87)
1752                 printf("[D%u]", ((uint32_t)t) - 0x80);
1753         else if (t >= 0x88 && t <= 0x8F)
1754                 printf("[A%u]", ((uint32_t)t) - 0x88);
1755         else
1756                 printf("[%X:%c]", (uint32_t)t, (char)t);
1757 }
1758
1759
1760 void DumpTokenBuffer(void)
1761 {
1762         printf("Tokens [%X]: ", sloc);
1763
1764         for(TOKEN * t=tokbuf; *t!=EOL; t++)
1765         {
1766                 if (*t == COLON)
1767                         printf("[COLON]");
1768                 else if (*t == CONST)
1769                 {
1770                         PTR tp;
1771                         tp.u32 = t + 1;
1772                         printf("[CONST: $%lX]", *tp.u64);
1773                         t += 2;
1774                 }
1775                 else if (*t == FCONST)
1776                 {
1777                         PTR tp;
1778                         tp.u32 = t + 1;
1779                         printf("[FCONST: $%lX]", *tp.u64);
1780                         t += 2;
1781                 }
1782                 else if (*t == ACONST)
1783                 {
1784                         printf("[ACONST: $%X, $%X]", (uint32_t)t[1], (uint32_t)t[2]);
1785                         t += 2;
1786                 }
1787                 else if (*t == STRING)
1788                 {
1789                         t++;
1790                         printf("[STRING:\"%s\"]", string[*t]);
1791                 }
1792                 else if (*t == SYMBOL)
1793                 {
1794                         t++;
1795                         printf("[SYMBOL:\"%s\"]", string[*t]);
1796                 }
1797                 else if (*t == EOS)
1798                         printf("[EOS]");
1799                 else if (*t == TKEOF)
1800                         printf("[TKEOF]");
1801                 else if (*t == DEQUALS)
1802                         printf("[DEQUALS]");
1803                 else if (*t == SET)
1804                         printf("[SET]");
1805                 else if (*t == REG)
1806                         printf("[REG]");
1807                 else if (*t == DCOLON)
1808                         printf("[DCOLON]");
1809                 else if (*t == GE)
1810                         printf("[GE]");
1811                 else if (*t == LE)
1812                         printf("[LE]");
1813                 else if (*t == NE)
1814                         printf("[NE]");
1815                 else if (*t == SHR)
1816                         printf("[SHR]");
1817                 else if (*t == SHL)
1818                         printf("[SHL]");
1819                 else if (*t == UNMINUS)
1820                         printf("[UNMINUS]");
1821                 else if (*t == DOTB)
1822                         printf("[DOTB]");
1823                 else if (*t == DOTW)
1824                         printf("[DOTW]");
1825                 else if (*t == DOTL)
1826                         printf("[DOTL]");
1827                 else if (*t == DOTQ)
1828                         printf("[DOTQ]");
1829                 else if (*t == DOTS)
1830                         printf("[DOTS]");
1831                 else if (*t == DOTD)
1832                         printf("[DOTD]");
1833                 else if (*t == DOTI)
1834                         printf("[DOTI]");
1835                 else if (*t == ENDEXPR)
1836                         printf("[ENDEXPR]");
1837                 else if (*t == CR_ABSCOUNT)
1838                         printf("[CR_ABSCOUNT]");
1839                 else if (*t == CR_DEFINED)
1840                         printf("[CR_DEFINED]");
1841                 else if (*t == CR_REFERENCED)
1842                         printf("[CR_REFERENCED]");
1843                 else if (*t == CR_STREQ)
1844                         printf("[CR_STREQ]");
1845                 else if (*t == CR_MACDEF)
1846                         printf("[CR_MACDEF]");
1847                 else if (*t == CR_TIME)
1848                         printf("[CR_TIME]");
1849                 else if (*t == CR_DATE)
1850                         printf("[CR_DATE]");
1851                 else if (*t >= 0x20 && *t <= 0x2F)
1852                         printf("[%c]", (char)*t);
1853                 else if (*t >= 0x3A && *t <= 0x3F)
1854                         printf("[%c]", (char)*t);
1855                 else if (*t >= 0x80 && *t <= 0x87)
1856                         printf("[D%u]", ((uint32_t)*t) - 0x80);
1857                 else if (*t >= 0x88 && *t <= 0x8F)
1858                         printf("[A%u]", ((uint32_t)*t) - 0x88);
1859                 else
1860                         printf("[%X:%c]", (uint32_t)*t, (char)*t);
1861         }
1862
1863         printf("[EOL]\n");
1864 }
1865