Various cleanups to fix compiler warnings.
[rmac] / token.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // TOKEN.C - Token Handling
4 // Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source Utilised with the Kind Permission of Landon Dyer
7 //
8
9 #include "token.h"
10 #include "symbol.h"
11 #include "procln.h"
12 #include "macro.h"
13 #include "error.h"
14
15 #define DECL_KW                                             // Declare keyword arrays
16 #define DEF_KW                                              // Declare keyword values 
17 #include "kwtab.h"                                          // Incl generated keyword tables & defs
18
19 int lnsave;                                                 // 1; strcpy() text of current line
20 int curlineno;                                              // Current line number
21 int totlines;                                               // Total # of lines
22 int mjump_align = 0;                                        // mjump alignment flag
23 char lntag;                                                 // Line tag
24 char * curfname;                                            // Current filename
25 char tolowertab[128];                                       // Uppercase ==> lowercase 
26 char hextab[128];                                           // Table of hex values
27 char dotxtab[128];                                          // Table for ".b", ".s", etc.
28 char irbuf[LNSIZ];                                          // Text for .rept block line
29 char lnbuf[LNSIZ];                                          // Text of current line
30 WORD filecount;                                             // Unique file number counter
31 WORD cfileno;                                               // Current file number
32 TOKEN * tok;                                                // Ptr to current token
33 TOKEN * etok;                                               // Ptr past last token in tokbuf[]
34 TOKEN tokeol[1] = {EOL};                                    // Bailout end-of-line token
35
36 // File record, used to maintain a list of every include file ever visited
37 #define FILEREC struct _filerec
38 FILEREC
39 {
40    FILEREC * frec_next;
41    char * frec_name;
42 };
43
44 FILEREC * filerec;
45 FILEREC * last_fr;
46
47 INOBJ * cur_inobj;                                          // Ptr current input obj (IFILE/IMACRO)
48 static INOBJ * f_inobj;                                     // Ptr list of free INOBJs
49 static IFILE * f_ifile;                                     // Ptr list of free IFILEs
50 static IMACRO * f_imacro;                                   // Ptr list of free IMACROs
51
52 static TOKEN tokbuf[TOKBUFSIZE];                            // Token buffer (stack-like, all files)
53
54 char chrtab[] = {
55    ILLEG, ILLEG, ILLEG, ILLEG,                                    // NUL SOH STX ETX 
56    ILLEG, ILLEG, ILLEG, ILLEG,                                    // EOT ENQ ACK BEL 
57    ILLEG, WHITE, ILLEG, ILLEG,                                    // BS HT LF VT 
58    WHITE, ILLEG, ILLEG, ILLEG,                                    // FF CR SO SI 
59
60    ILLEG, ILLEG, ILLEG, ILLEG,                                    // DLE DC1 DC2 DC3 
61    ILLEG, ILLEG, ILLEG, ILLEG,                                    // DC4 NAK SYN ETB 
62    ILLEG, ILLEG, ILLEG, ILLEG,                                    // CAN EM SUB ESC 
63    ILLEG, ILLEG, ILLEG, ILLEG,                                    // FS GS RS US 
64
65    WHITE, MULTX, MULTX, SELF,                                     // SP ! " #
66    MULTX+CTSYM, MULTX, SELF, MULTX,                               // $ % & '
67    SELF, SELF, SELF, SELF,                                        // ( ) * +
68    SELF, SELF, STSYM, SELF,                                       // , - . /
69
70    DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,                        // 0 1 
71    DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,                        // 2 3 
72    DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,                        // 4 5 
73    DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,                        // 6 7 
74    DIGIT+HDIGIT+CTSYM, DIGIT+HDIGIT+CTSYM,                        // 8 9 
75    MULTX, MULTX,                                                  // : ; 
76    MULTX, MULTX, MULTX, STSYM+CTSYM,                              // < = > ? 
77
78    MULTX, STSYM+CTSYM+HDIGIT,                                     // @ A
79    (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,              // B C
80    STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,                        // D E
81    STSYM+CTSYM+HDIGIT, STSYM+CTSYM,                               // F G
82    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM,            // H I J K
83    (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM,  // L M N O
84
85    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM,  // P Q R S
86    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM,  // T U V W
87    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF,                   // X Y Z [
88    SELF, SELF, MULTX, STSYM+CTSYM,                                // \ ] ^ _
89
90    ILLEG, STSYM+CTSYM+HDIGIT,                                     // ` a
91    (char)((BYTE)DOT)+STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,              // b c
92    STSYM+CTSYM+HDIGIT, STSYM+CTSYM+HDIGIT,                        // d e
93    STSYM+CTSYM+HDIGIT, STSYM+CTSYM,                               // f g
94    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM,            // h i j k
95    (char)((BYTE)DOT)+STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM,  // l m n o
96
97    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM,  // p q r s 
98    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, (char)((BYTE)DOT)+STSYM+CTSYM,  // t u v w 
99    STSYM+CTSYM, STSYM+CTSYM, STSYM+CTSYM, SELF,                   // x y z { 
100    SELF, SELF, SELF, ILLEG                                        // | } ~ DEL 
101 };
102
103 // Names of registers
104 static char * regname[] = {
105    "d0", "d1",  "d2",  "d3", "d4", "d5", "d6", "d7",
106    "a0", "a1",  "a2",  "a3", "a4", "a5", "a6", "a7",
107    "pc", "ssp", "usp", "sr", "ccr"
108 };
109
110 static char * riscregname[] = {
111     "r0",  "r1",  "r2",  "r3",  "r4", "r5",   "r6",  "r7", 
112     "r8",  "r9", "r10", "r11", "r12", "r13", "r14", "r15",
113    "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
114    "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
115 };
116
117 //
118 // --- Make `fnum' the Current `curfname' ----------------------------------------------------------
119 //
120
121 void setfnum(WORD fnum)
122 {
123    FILEREC *fr;
124
125    for(fr = filerec; fr != NULL && fnum--; fr = fr->frec_next)
126       ;
127
128    if(fr == NULL)
129       curfname = "(*top*)";
130    else
131       curfname = fr->frec_name;
132 }
133
134 //
135 // --- Allocate an IFILE or IMACRO -----------------------------------------------------------------
136 //
137
138 INOBJ * a_inobj(int typ)
139 {
140    INOBJ *inobj;
141    IFILE *ifile;
142    IMACRO *imacro;
143
144    // Allocate and initialize INOBJ first
145    if(f_inobj == NULL)
146       inobj = (INOBJ *)amem((LONG)sizeof(INOBJ));
147    else {
148       inobj = f_inobj;
149       f_inobj = f_inobj->in_link;
150    }
151
152    switch(typ) {
153       case SRC_IFILE:                                       // Alloc and init an IFILE
154          if(f_ifile == NULL)
155             ifile = (IFILE *)amem((LONG)sizeof(IFILE));
156          else {
157             ifile = f_ifile;
158             f_ifile = f_ifile->if_link;
159          }
160          inobj->inobj.ifile = ifile;
161          break;
162       case SRC_IMACRO:                                      // Alloc and init an IMACRO 
163          if(f_imacro == NULL)
164             imacro = (IMACRO *)amem((LONG)sizeof(IMACRO));
165          else {
166             imacro = f_imacro;
167             f_imacro = f_imacro->im_link;
168          }
169          inobj->inobj.imacro = imacro;
170          break;
171       case SRC_IREPT:                                       // Alloc and init an IREPT
172          inobj->inobj.irept = (IREPT *)amem((LONG)sizeof(IREPT));
173          DEBUG printf("alloc IREPT\n");
174          break;
175    }
176
177    // Install INOBJ on top of input stack
178    inobj->in_ifent = ifent;                                 // Record .if context on entry
179    inobj->in_type = (WORD)typ;
180    inobj->in_otok = tok;
181    inobj->in_etok = etok;
182    inobj->in_link = cur_inobj;
183    cur_inobj = inobj;
184
185    return(inobj);
186 }
187
188 //
189 // -------------------------------------------------------------------------------------------------
190 // Perform macro substitution from 'orig' to 'dest'. Return OK or some error.
191 // A macro reference is in one of two forms:
192 // \name <non-name-character>
193 // \{name}
194 // A doubled backslash (\\) is compressed to a single backslash (\).
195 // Argument definitions have been pre-tokenized, so we have to turn them back into text.  This 
196 // means that numbers, in particular, become hex, regardless of their representation when the macro
197 // was invoked. This is a hack. 
198 // A label may appear at the beginning of the line:
199 // :<name><whitespace>
200 // (the colon must be in the first column).  These labels are stripped before macro expansion takes
201 // place.
202 // -------------------------------------------------------------------------------------------------
203 //
204
205 int mexpand(char * src, char * dest, int destsiz)
206 {
207    char *s;
208    char *d = NULL;
209    char *dst;                                               // Next dest slot
210    char *edst;                                              // End+1 of dest buffer
211    int i;
212    int questmark;                                           // \? for testing argument existence
213    TOKEN *tk;
214    char mname[128];                                         // Assume max size of a formal arg name
215    int macnum;
216    SYM *arg;
217    IMACRO *imacro;
218    char numbuf[20];                                         // Buffer for text of CONSTs
219
220    imacro = cur_inobj->inobj.imacro;
221    macnum = (int)(imacro->im_macro->sattr);
222
223    --destsiz;
224    dst = dest;
225    edst = dest + destsiz;
226
227    // Check for (and skip over) any "label" on the line
228    s = src;
229    if(*s == ':') {
230       while(*s != EOS && !(chrtab[*s] & WHITE)) ++s;
231       if(*s != EOS) ++s;                                    // Skip first whitespace
232    }
233
234    // Expand the rest of the line
235    while(*s != EOS)
236       if(*s != '\\') {                                      // Copy single character
237          if(dst >= edst)
238             goto overflow;
239          *dst++ = *s++;
240       } else {                                              // Do macro expansion
241          questmark = 0;
242
243          // Do special cases
244          switch(*++s) {
245             case '\\':                                      // \\, \ (collapse to single backslash)
246                if(dst >= edst)
247                   goto overflow;
248                *dst++ = *s++;
249                continue;
250             case '?':                                       // \? <macro>  set `questmark' flag 
251                ++s;
252                questmark = 1;
253                break;
254             case '#':                                       // \#, number of arguments 
255                sprintf(numbuf, "%d", (int)imacro->im_nargs);
256                goto copystr;
257             case '!':                                       // \! size suffix supplied on invocation
258                switch((int)imacro->im_siz) {
259                   case SIZN: d = "";   break;
260                   case SIZB: d = ".b"; break;
261                   case SIZW: d = ".w"; break;
262                   case SIZL: d = ".l"; break;
263                }
264                goto copy_d;
265             case '~':                                       // ==> unique label string Mnnnn... 
266                sprintf(numbuf, "M%ld", curuniq);
267                
268                copystr:
269
270                d = numbuf;
271
272                copy_d:
273
274                ++s;
275                while(*d != EOS)
276                   if(dst >= edst) goto overflow;
277                   else *dst++ = *d++;
278                continue;
279             case EOS:
280                return(error("missing argument name"));
281          }
282
283          // \n ==> argument number 'n', 0..9
284          if(chrtab[*s] & DIGIT) {
285             i = *s++ - '1';
286             if(i < 0)
287                i = 9;
288             goto arg_num;
289          }
290
291          // Get argument name: \name, \{name}
292          d = mname;
293          if(*s != '{') {                                    // \foo
294             do 
295                *d++ = *s++;
296             while(chrtab[*s] & CTSYM);
297          } else {                                           // \\{foo} 
298             for(++s; *s != EOS && *s != '}';)
299                *d++ = *s++;
300             if(*s != '}') return(error("missing '}'"));
301             else ++s;
302          }
303          *d = EOS;
304
305          // Lookup the argument and copy its (string) value into the destination string
306                         DEBUG printf("mname='%s'\n", mname);
307          if((arg = lookup(mname, MACARG, macnum)) == NULL)
308             return(errors("undefined argument: '%s'", mname));
309          else {
310             // Convert a string of tokens (terminated with EOL) back into text. If an argument 
311             // is out of range (not specified in the macro invocation) then it is ignored.
312             i = (int)arg->svalue;
313
314             arg_num:
315
316             DEBUG printf("~argnumber=%d\n", i);
317
318             tk = NULL;
319             if(i < imacro->im_nargs)
320                tk = argp[i];
321
322             // \?arg yields:
323             //    0  if the argument is empty or non-existant,
324             //    1  if the argument is not empty
325             if(questmark) {
326                if(tk == NULL || *tk == EOL)
327                   questmark = 0;
328                if(dst >= edst)
329                   goto overflow;
330                *dst++ = (char)(questmark + '0');
331                continue;
332             }
333
334             if(tk != NULL)                                  // arg# is in range, so expand it
335                while(*tk != EOL) {
336                   // Reverse-translation from a token number to a string.  This is a hack.
337                   // It might be better table-driven.
338                   d = NULL;
339                   if((*tk >= KW_D0) && !rdsp && !rgpu) {
340                      d = regname[(int)*tk++ - KW_D0];
341                      goto strcopy;
342                   } else if((*tk >= KW_R0) && (*tk <= KW_R31)) {
343                      d = riscregname[(int)*tk++ - KW_R0];
344                      goto strcopy;
345                   } else 
346                      switch((int)*tk++) {
347                         case SYMBOL:
348                            d = (char *)*tk++;
349                            break;
350                         case STRING:
351                            d = (char *)*tk++;
352
353                            if(dst >= edst)
354                               goto overflow;
355                            *dst++ = '"';
356
357                            while(*d != EOS)
358                               if(dst >= edst)
359                                  goto overflow;
360                               else *dst++ = *d++;
361
362                            if(dst >= edst)
363                               goto overflow;
364                            *dst++ = '"';
365                            continue;
366                            break;
367                         case CONST:
368                            sprintf(numbuf, "$%lx", (LONG)*tk++);
369                            d = numbuf;
370                            break;
371                         case DEQUALS:
372                            d = "==";
373                            break;
374                         case SET:
375                            d = "set";
376                            break;
377                         case COLON:
378                            d = ":";
379                            break;
380                         case DCOLON:
381                            d = "::";
382                            break;
383                         case GE:
384                            d = ">=";
385                            break;
386                         case LE:
387                            d = "<=";
388                            break;
389                         case NE:
390                            d = "<>";
391                            break;
392                         case SHR:
393                            d = ">>";
394                            break;
395                         case SHL:
396                            d = "<<";
397                            break;
398                         case DOTB:
399                            d = ".b";
400                            break;
401                         case DOTW:
402                            d = ".w";
403                            break;
404                         case DOTL:
405                            d = ".l";
406                            break;
407                         case CR_DATE:
408                            d = "^^date";
409                            break;
410                         case CR_TIME:
411                            d = "^^time";
412                            break;
413                         case CR_DEFINED:
414                            d = "^^defined ";
415                            break;
416                         case CR_REFERENCED:
417                            d = "^^referenced ";
418                            break;
419                         case CR_STREQ:
420                            d = "^^streq ";
421                            break;
422                         case CR_MACDEF:
423                            d = "^^macdef ";
424                            break;
425                         default:
426                            if(dst >= edst)
427                               goto overflow;
428                            *dst++ = (char)*(tk-1);
429                            break;
430                      }
431
432                   // If 'd' != NULL, copy string to destination
433                   if(d != NULL) {
434
435                      strcopy:
436
437                      DEBUG printf("d='%s'\n", d);
438                      while(*d != EOS)
439                         if(dst >= edst)
440                            goto overflow;
441                         else *dst++ = *d++;
442                   }
443                }
444          }
445       }
446
447         
448    *dst = EOS;
449    return(OK);
450
451    overflow:
452
453    *dst = EOS;
454    return(fatal("line too long as a result of macro expansion"));
455 }
456
457 //
458 // --- Get Next Line of Text from a Macro ----------------------------------------------------------
459 //
460
461 char * getmln(void)
462 {
463    IMACRO *imacro;
464    LONG *strp;
465    unsigned source_addr;
466
467    imacro = cur_inobj->inobj.imacro;
468    strp = imacro->im_nextln;
469
470    if(strp == NULL)                                         // End-of-macro
471       return(NULL);
472
473    imacro->im_nextln = (LONG *)*strp;
474    mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
475
476    if(!strcmp(imacro->im_macro->sname, "mjump") && !mjump_align) {
477       // if we need to adjust the alignment of the jump source address to meet the rules of
478       // gpu main execution we need to skip the first nop of the macro. This is simpler than
479       // trying to insert nop's mid macro.
480       source_addr = (orgactive) ? orgaddr : sloc;
481       source_addr += 8;
482       if(source_addr % 4) {
483          strp = imacro->im_nextln;
484          if(strp == NULL) return(NULL);
485          imacro->im_nextln = (LONG *)*strp;
486          mexpand((char *)(strp + 1), imacro->im_lnbuf, LNSIZ);
487       }
488       mjump_align = 1;
489    }
490
491    return(imacro->im_lnbuf);
492 }
493
494 //
495 // --- Get Next Line of Text from a Repeat Block ---------------------------------------------------
496 //
497  
498 char * getrln(void)
499 {
500    IREPT *irept;
501    LONG *strp;
502
503    irept = cur_inobj->inobj.irept;
504    strp = irept->ir_nextln;         // initial null
505
506    // Do repeat at end of .rept block's string list
507    if(strp == NULL) {
508       DEBUG printf("back-to-top-of-repeat-block count=%d\n", (int)irept->ir_count);
509       irept->ir_nextln = irept->ir_firstln;  // copy first line
510       if(irept->ir_count-- == 0) {
511          DEBUG printf("end-repeat-block\n");
512          return(NULL);
513       }
514       strp = irept->ir_nextln;               //strp
515    }
516
517    strcpy(irbuf, (char*)(irept->ir_nextln + 1));
518
519    DEBUG printf("repeat line='%s'\n", irbuf);
520    irept->ir_nextln = (LONG *)*strp;
521
522    return(irbuf);
523 }
524
525 //
526 // --- Include a Source File used at the Root, and for ".include" Files ----------------------------
527 //
528
529 int include(int handle, char * fname)
530 {
531    IFILE *ifile;
532    INOBJ *inobj;
533    FILEREC *fr;
534
535    if(verb_flag) printf("[Including: %s]\n", fname);        // Verbose mode
536
537    // Alloc and initialize include-descriptors
538    inobj = a_inobj(SRC_IFILE);
539    ifile = inobj->inobj.ifile;
540
541    ifile->ifhandle = handle;                                // Setup file handle
542    ifile->ifind = ifile->ifcnt = 0;                         // Setup buffer indices
543    ifile->ifoldlineno = curlineno;                          // Save old line number
544    ifile->ifoldfname = curfname;                            // Save old filename
545    ifile->ifno = cfileno;                                   // Save old file number
546    cfileno = ++filecount;                                   // Compute new file number
547    curfname = nstring(fname);                                 // Set current filename (alloc storage)
548    curlineno = 0;                                           // Start on line zero
549
550    // Add another file to the file-record
551    fr = (FILEREC *)amem((LONG)sizeof(FILEREC));
552    fr->frec_next = NULL;
553    fr->frec_name = curfname;
554    if(last_fr == NULL)
555       filerec = fr;                                         // Add first filerec 
556    else
557       last_fr->frec_next = fr;                              // Append to list of filerecs 
558    last_fr = fr;
559
560    return(OK);
561 }
562
563 //
564 // --- Initialize Tokenizer ------------------------------------------------------------------------
565 //
566
567 void init_token(void)
568 {
569    int i;                                                   // Iterator
570    char *htab = "0123456789abcdefABCDEF";                   // Hex character table
571
572    lnsave = 0;                                              // Don't save lines
573    curfname = "";                                           // No file, empty filename
574    filecount = (WORD)-1;
575    cfileno = (WORD)-1;                                      // cfileno gets bumped to 0
576    curlineno = 0;
577    totlines = 0;
578    etok = tokbuf;
579    f_inobj = NULL;
580    f_ifile = NULL;
581    f_imacro = NULL;
582    cur_inobj = NULL;
583    filerec = NULL;
584    last_fr = NULL;
585    lntag = SPACE;
586
587    // Initialize hex, "dot" and tolower tables
588    for(i = 0; i < 128; ++i) {
589       hextab[i] = -1;
590       dotxtab[i] = 0;
591       tolowertab[i] = (char)i;
592    }
593    for(i = 0; htab[i] != EOS; ++i)
594       hextab[htab[i]] = (char)((i < 16) ? i : i - 6);
595    for(i = 'A'; i <= 'Z'; ++i)
596       tolowertab[i] |= 0x20;
597
598    // These characters are legal immediately after a period
599    dotxtab['b'] = DOTB;                                     // .b .B .s .S 
600    dotxtab['B'] = DOTB;
601    dotxtab['s'] = DOTB;
602    dotxtab['S'] = DOTB;
603    dotxtab['w'] = DOTW;                                     // .w .W 
604    dotxtab['W'] = DOTW;
605    dotxtab['l'] = DOTL;                                     // .l .L 
606    dotxtab['L'] = DOTL;
607    dotxtab['I'] = DOTI;                                     // .l .L 
608    dotxtab['I'] = DOTI;
609 }
610
611 //
612 // --- Pop the Current Input Level -----------------------------------------------------------------
613 //
614 int fpop(void)
615 {
616    INOBJ *inobj;
617    IFILE *ifile;
618    IMACRO *imacro;
619    LONG *p, *p1;
620
621    inobj = cur_inobj;
622    if(inobj != NULL) {
623       // Pop IFENT levels until we reach the conditional assembly context we were at when the 
624       // input object was entered.
625       while(ifent != inobj->in_ifent)
626          d_endif();
627
628       tok = inobj->in_otok;                                 // Restore tok and otok
629       etok = inobj->in_etok;
630
631       switch(inobj->in_type) {
632          case SRC_IFILE:                                    // Pop and release an IFILE
633             if(verb_flag)
634                printf("[Leaving: %s]\n", curfname);
635             ifile = inobj->inobj.ifile;
636             ifile->if_link = f_ifile;
637             f_ifile = ifile;
638             close(ifile->ifhandle);                         // Close source file
639             curfname = ifile->ifoldfname;                   // Set current filename
640             curlineno = ifile->ifoldlineno;                 // Set current line# 
641             DEBUG printf("cfileno=%d ifile->ifno=%d\n", (int)cfileno, (int)ifile->ifno);
642             cfileno = ifile->ifno;                          // Restore current file number
643             break;
644          case SRC_IMACRO:                                   // Pop and release an IMACRO
645             imacro = inobj->inobj.imacro;
646             imacro->im_link = f_imacro;
647             f_imacro = imacro;
648             break;
649          case SRC_IREPT:                                    // Pop and release an IREPT
650             DEBUG printf("dealloc IREPT\n");
651             p = inobj->inobj.irept->ir_firstln;
652             while(p != NULL) {
653                p1 = (LONG *)*p;
654                p = p1;
655             }
656             break;
657       }
658
659       cur_inobj = inobj->in_link;
660       inobj->in_link = f_inobj;
661       f_inobj = inobj;
662    }
663
664    return(0);
665 }
666
667 //
668 // --- Get line from file into buf, return NULL on EOF or ptr to the start of a null-term line -----
669 //
670
671 char * getln(void)
672 {
673    IFILE *fl;
674    int i, j;
675    char *p, *d;
676    int readamt;
677
678    readamt = -1;                                            // 0 if last read() yeilded 0 bytes
679    fl = cur_inobj->inobj.ifile;
680
681    for(;;) {
682       // Scan for next end-of-line; handle stupid text formats by treating \r\n the same as \n.
683       // (lone '\r' at end of buffer means we have to check for '\n').
684       i = 0;
685       j = fl->ifcnt;
686       d = &fl->ifbuf[fl->ifind];
687
688       for(p = d; i < j; ++i, ++p) {
689          if(*p == '\r' || *p == '\n') {
690             ++i;
691             if(*p == '\r') {
692                if(i >= j) {
693                   break;                                    // Look for '\n' to eat 
694                } else if(p[1] == '\n') {
695                   ++i;
696                }
697             }
698
699             *p = '\0';
700
701             fl->ifind += i;
702             fl->ifcnt -= i;
703             return(d);
704          }
705       }
706
707       // Handle hanging lines by ignoring them (Input file is exhausted, no \r or \n on last line)
708       if(!readamt && fl->ifcnt) {
709          fl->ifcnt = 0;
710          *p = '\0';
711          return(NULL);
712       }
713
714       // Truncate and return absurdly long lines.
715       if(fl->ifcnt >= QUANTUM) {
716          fl->ifbuf[fl->ifind + fl->ifcnt - 1] = '\0';
717          fl->ifcnt = 0;
718          return(&fl->ifbuf[fl->ifind]);
719       }
720
721       // Relocate what's left of a line to the beginning of the buffer, and read some more of the 
722       // file in; return NULL if the buffer's empty and on EOF.
723       if(fl->ifind != 0) {
724          p = &fl->ifbuf[fl->ifind];
725          d = &fl->ifbuf[fl->ifcnt & 1];
726          for(i = 0; i < fl->ifcnt; ++i)
727             *d++ = *p++;
728          fl->ifind = fl->ifcnt & 1;
729       }
730
731       if((readamt = read(fl->ifhandle, &fl->ifbuf[fl->ifind + fl->ifcnt], QUANTUM)) < 0)
732          return(NULL);
733
734       if((fl->ifcnt += readamt) == 0)
735          return(NULL);
736    }
737 }
738
739 //
740 // --- Tokenize a Line -----------------------------------------------------------------------------
741 //
742
743 int tokln(void)
744 {
745    char *ln = NULL;                                         // Ptr to current position in line
746    char *p;                                                 // Random character ptr
747    TOKEN *tk;                                               // Token-deposit ptr
748    int state = 0;                                           // State for keyword detector
749    int j = 0;                                               // Var for keyword detector
750    char c;                                                  // Random char
751    VALUE v;                                                 // Random value
752    char *nullspot = NULL;                                   // Spot to clobber for SYMBOL terminatn
753    int stuffnull;                                           // 1:terminate SYMBOL '\0' at *nullspot
754    char c1;
755
756    retry:
757
758    if(cur_inobj == NULL)                                    // Return EOF if input stack is empty
759       return(TKEOF);
760
761    // Get another line of input from the current input source: a file, a macro, or a repeat-block
762    switch(cur_inobj->in_type) {
763       // Include-file:
764       // o  handle EOF;
765       // o  bump source line number;
766       // o  tag the listing-line with a space;
767       // o  kludge lines generated by Alcyon C.
768       case SRC_IFILE:
769          if((ln = getln()) == NULL) {
770             fpop();                                         // Pop input level
771             goto retry;                                     // Try for more lines 
772          }
773          ++curlineno;                                       // Bump line number
774          lntag = SPACE;
775          if(as68_flag) {
776             // AS68 compatibility, throw away all lines starting with back-quotes, tildes, or '*'
777             // On other lines, turn the first '*' into a semi-colon.
778             if(*ln == '`' || *ln == '~' || *ln == '*') *ln = ';';
779             else for(p = ln; *p != EOS; ++p) {
780                if(*p == '*') {
781                   *p = ';';
782                   break;
783                }
784             }
785          }
786          break;
787
788       // Macro-block:
789       // o  Handle end-of-macro;
790       // o  tag the listing-line with an at (@) sign.
791       case SRC_IMACRO:
792          if((ln = getmln()) == NULL) {
793             exitmac();                                      // Exit macro (pop args, do fpop(), etc)
794             goto retry;                                     // Try for more lines...
795          }
796          lntag = '@';
797          break;
798
799       // Repeat-block:
800       // o  Handle end-of-repeat-block;
801       // o  tag the listing-line with a pound (#) sign.
802       case SRC_IREPT:
803          if((ln = getrln()) == NULL) {
804             fpop();
805             goto retry;
806          }
807          lntag = '#';
808          break;
809    }
810
811    // Save text of the line.  We only do this during listings and within macro-type blocks, 
812    // since it is expensive to unconditionally copy every line.
813    if(lnsave) strcpy(lnbuf, ln);
814
815    // General house-keeping
816         tok = tokeol;                                            // Set "tok" to EOL in case of error
817         tk = etok;                                               // Reset token ptr
818         stuffnull = 0;                                           // Don't stuff nulls
819         ++totlines;                                              // Bump total #lines assembled
820
821    // See if the entire line is a comment.  This is a win if the programmer puts in lots of comments
822    if(*ln == '*' || *ln == ';' || ((*ln == '/') && (*(ln+1) == '/'))) goto goteol;
823
824    // Main tokenization loop;
825    // o  skip whitespace;
826    // o  handle end-of-line;
827    // o  handle symbols;
828    // o  handle single-character tokens (operators, etc.);
829    // o  handle multiple-character tokens (constants, strings, etc.).
830    for(; *ln != EOS;) {
831       // Skip whitespace, handle EOL
832       while((int)chrtab[*ln] & WHITE)
833          ++ln;
834
835       // Handle EOL, comment with ';'
836       if(*ln == EOS || *ln == ';'|| ((*ln == '/') && (*(ln+1) == '/'))) 
837          break;
838
839       // Handle start of symbol. Symbols are null-terminated in place. The termination is
840       // always one symbol behind, since there may be no place for a null in the case that 
841       // an operator immediately follows the name.
842       c = chrtab[*ln];
843       if(c & STSYM) {
844          if(stuffnull)                                      // Terminate old symbol 
845             *nullspot = EOS;
846          v = 0;                                             // Assume no DOT attrib follows symbol
847          stuffnull = 1;
848          p = nullspot = ln++;                               // Nullspot -> start of this symbol
849
850          // Find end of symbol (and compute its length)
851          for(j = 1; (int)chrtab[*ln] & CTSYM; ++j)
852             ++ln;
853
854          // Handle "DOT" special forms (like ".b") that follow a normal symbol or keyword:
855          if(*ln == '.') {
856             *ln++ = EOS;                                    // Terminate symbol
857             stuffnull = 0;                                  // And never try it again 
858
859             // Character following the `.' must have a DOT attribute, and the chararacter after 
860             // THAT one must not have a start-symbol attribute (to prevent symbols that look
861             // like, for example, "zingo.barf", which might be a good idea anyway....)
862             if((((int)chrtab[*ln] & DOT) == 0) || ((int)dotxtab[*ln] <= 0))
863                return(error("[bwsl] must follow `.' in symbol"));
864             v = (VALUE)dotxtab[*ln++];
865             if((int)chrtab[*ln] & CTSYM)
866                return(error("misuse of `.', not allowed in symbols"));
867          }
868
869          // If the symbol is small, check to see if it's really the name of a register.
870          if(j <= KWSIZE) {
871             for(state = 0; state >= 0;) {
872                j = (int)tolowertab[*p++];
873                j += kwbase[state];
874                if(kwcheck[j] != state) {
875                   j = -1;
876                   break;
877                }
878
879                if(*p == EOS || p == ln) {
880                   j = kwaccept[j];
881                   break;
882                }
883
884                state = kwtab[j];
885             }
886          } else {
887             j = -1;
888          }
889
890          //make j = -1 if time, date etc with no preceeding ^^
891          //defined, referenced, streq, macdef, date and time
892          switch((TOKEN)j) {
893             case 112:   // defined
894             case 113:   // referenced
895             case 118:   // streq
896             case 119:   // macdef
897             case 120:   // time
898             case 121:   // date
899                j = -1;
900                break;
901          }
902
903          if(j < 0 || state < 0) {
904             *tk++ = SYMBOL;
905             *tk++ = (TOKEN)nullspot;
906          } else {
907             *tk++ = (TOKEN)j;
908             stuffnull = 0;
909          }
910
911          if(v)                                              // Record attribute token (if any)
912             *tk++ = (TOKEN)v;
913
914          if(stuffnull)                                      // Arrange for string termination 
915             nullspot = ln;
916          continue;
917       }
918
919       // Handle identity tokens
920       if(c & SELF) {
921          *tk++ = *ln++;
922          continue;
923       }
924
925       // Handle multiple-character tokens
926       if(c & MULTX) {
927          switch(*ln++) {
928             case '!':                                       // ! or != 
929                if(*ln == '=') {
930                   *tk++ = NE;
931                   ++ln;
932                } else *tk++ = '!';
933                continue;
934             case '\'':                                      // 'string' 
935             case '\"':                                      // "string" 
936                c1 = ln[-1];
937                *tk++ = STRING;
938                *tk++ = (TOKEN)ln;
939
940                for(p = ln; *ln != EOS && *ln != c1;) {
941                   c = *ln++;
942                   if(c == '\\')
943                      switch(*ln++) {
944                         case EOS:
945                            return(error("unterminated string"));
946                         case 'e':
947                            c = '\033';
948                            break;
949                         case 'n':
950                            c = '\n';
951                            break;
952                         case 'b':
953                            c = '\b';
954                            break;
955                         case 't':
956                            c = '\t';
957                            break;
958                         case 'r':
959                            c = '\r';
960                            break;
961                         case 'f':
962                            c = '\f';
963                            break;
964                         case '\"':
965                            c = '\"';
966                            break;
967                         case '\'':
968                            c = '\'';
969                            break;
970                         case '\\':
971                            c = '\\';
972                            break;
973                         default:
974                            warn("bad backslash code in string");
975                            --ln;
976                            break;
977                      }
978                   *p++ = c;
979                }
980
981                if(*ln++ != c1)
982                   return(error("unterminated string"));
983                *p++ = EOS;
984                continue;
985             case '$':                                       // $, hex constant
986                if((int)chrtab[*ln] & HDIGIT) {
987                   v = 0;
988                   while((int)hextab[*ln] >= 0)
989                      v = (v << 4) + (int)hextab[*ln++];
990                   if(*ln == '.') {
991                      if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
992                      if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
993                      if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
994                   }
995                   *tk++ = CONST;
996                   *tk++ = v;
997                } else *tk++ = '$';
998                continue;
999             case '<':                                       // < or << or <> or <= 
1000                switch(*ln) {
1001                   case '<':
1002                      *tk++ = SHL;
1003                      ++ln;
1004                      continue;
1005                   case '>':
1006                      *tk++ = NE;
1007                      ++ln;
1008                      continue;
1009                   case '=':
1010                      *tk++ = LE;
1011                      ++ln;
1012                      continue;
1013                   default:
1014                      *tk++ = '<';
1015                      continue;
1016                }
1017             case ':':                                       // : or ::
1018                if(*ln == ':') {
1019                   *tk++ = DCOLON;
1020                   ++ln;
1021                } else *tk++ = ':';
1022                continue;
1023             case '=':                                       // = or == 
1024                if(*ln == '=') {
1025                   *tk++ = DEQUALS;
1026                   ++ln;
1027                } else *tk++ = '=';
1028                continue;
1029             case '>':                                       // > or >> or >= 
1030                switch(*ln) {
1031                   case '>':
1032                      *tk++ = SHR;
1033                      ++ln;
1034                      continue;
1035                   case '=':
1036                      *tk++ = GE;
1037                      ++ln;
1038                      continue;
1039                   default:
1040                      *tk++ = '>';
1041                      continue;
1042                }
1043             case '%':                                       // % or binary constant 
1044                if(*ln < '0' || *ln > '1') {
1045                   *tk++ = '%';
1046                   continue;
1047                }
1048                v = 0;
1049                while(*ln >= '0' && *ln <= '1')
1050                   v = (v << 1) + *ln++ - '0';
1051                if(*ln == '.') {
1052                   if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1053                   if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1054                   if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1055                }
1056                *tk++ = CONST;
1057                *tk++ = v;
1058                continue;
1059             case '@':                                       // @ or octal constant 
1060                if(*ln < '0' || *ln > '7') {
1061                   *tk++ = '@';
1062                   continue;
1063                }
1064                v = 0;
1065                while(*ln >= '0' && *ln <= '7')
1066                   v = (v << 3) + *ln++ - '0';
1067                if(*ln == '.') {
1068                   if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1069                   if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1070                   if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1071                }
1072                *tk++ = CONST;
1073                *tk++ = v;
1074                continue;
1075             case '^':                                       // ^ or ^^ <operator-name>
1076                if(*ln != '^') {
1077                   *tk++ = '^';
1078                   continue;
1079                }
1080
1081                if(((int)chrtab[*++ln] & STSYM) == 0) {
1082                   error("invalid symbol following ^^");
1083                   continue;
1084                }
1085
1086                p = ln++;
1087                while((int)chrtab[*ln] & CTSYM)
1088                   ++ln;
1089
1090                for(state = 0; state >= 0;) {
1091                   // Get char, convert to lowercase 
1092                   j = *p++;
1093                   if(j >= 'A' && j <= 'Z')
1094                      j += 0x20;
1095
1096                   j += kwbase[state];
1097                   if(kwcheck[j] != state) {
1098                      j = -1;
1099                      break;
1100                   }
1101
1102                   if(*p == EOS || p == ln) {
1103                      j = kwaccept[j];
1104                      break;
1105                   }
1106                   state = kwtab[j];
1107                }
1108
1109                if(j < 0 || state < 0) {
1110                   error("unknown symbol following ^^");
1111                   continue;
1112                }
1113
1114                *tk++ = (TOKEN)j;
1115                continue;
1116             default:
1117                interror(2);                                 // Bad MULTX entry in chrtab
1118                continue;
1119          }
1120       }
1121
1122                 
1123       // Handle decimal constant
1124       if(c & DIGIT) {
1125          v = 0;
1126          while((int)chrtab[*ln] & DIGIT)
1127             v = (v * 10) + *ln++ - '0';
1128          if(*ln == '.') {
1129             if((*(ln+1) == 'b') || (*(ln+1) == 'B')) { v &= 0x000000FF; ln += 2; }
1130             if((*(ln+1) == 'w') || (*(ln+1) == 'W')) { v &= 0x0000FFFF; ln += 2; }
1131             if((*(ln+1) == 'l') || (*(ln+1) == 'L')) { ln += 2; }
1132          }
1133          *tk++ = CONST;
1134          *tk++ = v;
1135          continue;
1136       }
1137
1138       // Handle illegal character
1139       return(error("illegal character"));
1140    }
1141
1142    // Terminate line of tokens and return "success."
1143
1144    goteol:
1145
1146    tok = etok;                                              // Set tok to beginning of line
1147    if(stuffnull)                                            // Terminate last SYMBOL
1148       *nullspot = EOS;
1149    *tk++ = EOL;
1150
1151    return(OK);
1152 }
1153
1154 //
1155 // -------------------------------------------------------------------------------------------------
1156 // .GOTO <label>        goto directive
1157 // 
1158 // The label is searched for starting from the first line of the current, enclosing macro 
1159 // definition.  If no enclosing macro exists, an error is generated.
1160 // 
1161 // A label is of the form:
1162 // 
1163 // :<name><whitespace>
1164 // 
1165 // The colon must appear in column 1.  The label is stripped prior to macro expansion, and is NOT
1166 // subject to macro expansion.  The whitespace may also be EOL.
1167 // -------------------------------------------------------------------------------------------------
1168 //
1169
1170 //int d_goto(WORD siz) {
1171 int d_goto(void)
1172 {
1173    char *sym;                                               // Label to search for 
1174    LONG *defln;                                             // Macro definition strings 
1175    char *s1;                                                // Temps for string comparison 
1176    char *s2;
1177    IMACRO *imacro;                                          // Macro invocation block
1178
1179    // Setup for the search
1180    if(*tok != SYMBOL) return(error("missing label"));
1181    sym = (char *)tok[1];
1182    tok += 2;
1183
1184    if(cur_inobj->in_type != SRC_IMACRO) return(error("goto not in macro"));
1185    imacro = cur_inobj->inobj.imacro;
1186    defln = (LONG *)imacro->im_macro->svalue;
1187
1188    // Find the label, starting with the first line.
1189    for(; defln != NULL; defln = (LONG *)*defln)
1190       if(*(char *)(defln + 1) == ':') {
1191          // Compare names (sleazo string compare)
1192          s1 = sym;
1193          s2 = (char *)(defln + 1) + 1;
1194          while(*s1 == *s2)
1195             if(*s1 == EOS) break;
1196             else {
1197                ++s1;
1198                ++s2;
1199             }
1200
1201          // Found the label, set new macro next-line and return.
1202          if((*s2 == EOS) || ((int)chrtab[*s2] & WHITE)) {
1203             imacro->im_nextln = defln;
1204             return(0);
1205          }
1206       }
1207
1208    return(error("goto label not found"));
1209 }