Initial commit.
[rmac] / rmac.c
1 //
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // RMAC.C - Main Application Code
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 "rmac.h"
10 #include "error.h"
11 #include "listing.h"
12 #include "procln.h"
13 #include "token.h"
14 #include "expr.h"
15 #include "sect.h"
16 #include "mark.h"
17 #include "macro.h"
18 #include "risca.h"
19 #include "direct.h"
20 #include "version.h"
21 #include "debug.h"
22 #include "symbol.h"
23 #include "object.h"
24
25 int perm_verb_flag;                                         // Permanently verbose, interactive mode
26 int list_flag;                                              // "-l" Listing flag on command line
27 int verb_flag;                                              // Be verbose about what's going on
28 int as68_flag;                                              // as68 kludge mode
29 int glob_flag;                                              // Assume undefined symbols are global
30 int lsym_flag;                                              // Include local symbols in object file
31 int sbra_flag;                                              // Warn about possible short branches
32 int obj_format;                                             // Object format flag
33 int debug;                                                  // [1..9] Enable debugging levels
34 int err_flag;                                               // '-e' specified
35 int err_fd;                                                 // File to write error messages to 
36 int rgpu, rdsp;                                             // Assembling Jaguar GPU or DSP code
37 int list_fd;                                                // File to write listing to
38 int regbank;                                                // RISC register bank
39 int segpadsize;                                             // Segment padding size
40 int in_main;                                                // In main memory flag for GPUMAIN
41 int endian;                                                 // Processor endianess
42 char *objfname;                                             // Object filename pointer
43 char *firstfname;                                           // First source filename
44 char *cmdlnexec;                                            // Executable name, pointer to ARGV[0]
45 char *searchpath;                                           // Search path for include files 
46 char defname[] = "noname.o";                                // Default output filename
47
48 // Under Windows and UNIX malloc() is an expensive call, so for small amounts of memory we allocate
49 // from a previously allocated buffer.
50
51 #define A_AMOUNT        4096                                // Amount to malloc() at a time
52 #define A_THRESH        64                                  // Use malloc() for amounts >= A_THRESH
53
54 static LONG a_amount;                                       // Amount left at a_ptr 
55 static char *a_ptr;                                         // Next free chunk
56 LONG amemtot;                                               // amem() total of requests
57
58 // Qsort; The THRESHold below is the insertion sort threshold, and has been adjusted
59 // for records of size 48 bytes.The MTHREShold is where we stop finding a better median.
60  
61 #define THRESH          4                                   // Threshold for insertion
62 #define MTHRESH         6                                   // Threshold for median
63
64 static int (*qcmp)();                                       // The comparison routine
65 static int qsz;                                             // Size of each record
66 static int thresh;                                          // THRESHold in chars 
67 static int mthresh;                                         // MTHRESHold in chars
68
69 // qst: Do a quicksort. First, find the median element, and put that one in the first place as 
70 // the discriminator.  (This "median" is just the median of the first, last and middle elements).
71 // (Using this median instead of the first element is a big win).  Then, the usual 
72 // partitioning/swapping, followed by moving the discriminator into the right place.  Then, 
73 // figure out the sizes of the two partions, do the smaller one recursively and the larger one 
74 // via a repeat of this code.  Stopping when there are less than THRESH elements in a partition
75 // and cleaning up with an insertion sort (in our caller) is a huge win. All data swaps are done 
76 // in-line, which is space-losing but time-saving. (And there are only three places where 
77 // this is done).
78
79 static int qst(char *base, char *max) {
80    char c, *i, *j, *jj;
81    int ii;
82    char *mid, *tmp;
83    long lo, hi;
84
85    /*
86          * At the top here, lo is the number of characters of elements in the
87          * current partition.  (Which should be max - base).
88          * Find the median of the first, last, and middle element and make
89          * that the middle element.  Set j to largest of first and middle.
90          * If max is larger than that guy, then it's that guy, else compare
91          * max with loser of first and take larger.  Things are set up to
92          * prefer the middle, then the first in case of ties.
93          */
94         lo = max - base;                /* number of elements as chars */
95         do      {
96                 mid = i = base + qsz * ((lo / qsz) >> 1);
97                 if (lo >= mthresh) {
98                         j = (qcmp((jj = base), i) > 0 ? jj : i);
99                         if (qcmp(j, (tmp = max - qsz)) > 0) {
100                                 /* switch to first loser */
101                                 j = (j == jj ? i : jj);
102                                 if (qcmp(j, tmp) < 0)
103                                         j = tmp;
104                         }
105                         if (j != i) {
106                                 ii = qsz;
107                                 do      {
108                                         c = *i;
109                                         *i++ = *j;
110                                         *j++ = c;
111                                 } while (--ii);
112                         }
113                 }
114                 /*
115                  * Semi-standard quicksort partitioning/swapping
116                  */
117                 for (i = base, j = max - qsz; ; ) {
118                         while (i < mid && qcmp(i, mid) <= 0)
119                                 i += qsz;
120                         while (j > mid) {
121                                 if (qcmp(mid, j) <= 0) {
122                                         j -= qsz;
123                                         continue;
124                                 }
125                                 tmp = i + qsz;  /* value of i after swap */
126                                 if (i == mid) {
127                                         /* j <-> mid, new mid is j */
128                                         mid = jj = j;
129                                 } else {
130                                         /* i <-> j */
131                                         jj = j;
132                                         j -= qsz;
133                                 }
134                                 goto swap;
135                         }
136                         if (i == mid) {
137                                 break;
138                         } else {
139                                 /* i <-> mid, new mid is i */
140                                 jj = mid;
141                                 tmp = mid = i;  /* value of i after swap */
142                                 j -= qsz;
143                         }
144 swap:
145                         ii = qsz;
146                         do      {
147                                 c = *i;
148                                 *i++ = *jj;
149                                 *jj++ = c;
150                         } while (--ii);
151                         i = tmp;
152                 }
153                 /*
154                  * Look at sizes of the two partitions, do the smaller
155                  * one first by recursion, then do the larger one by
156                  * making sure lo is its size, base and max are update
157                  * correctly, and branching back.  But only repeat
158                  * (recursively or by branching) if the partition is
159                  * of at least size THRESH.
160                  */
161                 i = (j = mid) + qsz;
162                 if ((lo = j - base) <= (hi = max - i)) {
163                         if (lo >= thresh)
164                                 qst(base, j);
165                         base = i;
166                         lo = hi;
167                 } else {
168                         if (hi >= thresh)
169                                 qst(i, max);
170                         max = j;
171                 }
172         } while (lo >= thresh);
173
174    return(0);
175 }
176
177 /*
178  * qsort:
179  * First, set up some global parameters for qst to share.  Then, quicksort
180  * with qst(), and then a cleanup insertion sort ourselves.  Sound simple?
181  * It's not...
182  */
183
184 int rmac_qsort(char *base, int n, int size, int (*compar)()) {
185         register char c, *i, *j, *lo, *hi;
186         char *min, *max;
187
188         if (n <= 1)
189                 return(0);
190         qsz = size;
191         qcmp = compar;
192         thresh = qsz * THRESH;
193         mthresh = qsz * MTHRESH;
194         max = base + n * qsz;
195         if (n >= THRESH) {
196                 qst(base, max);
197                 hi = base + thresh;
198         } else {
199                 hi = max;
200         }
201         /*
202          * First put smallest element, which must be in the first THRESH, in
203          * the first position as a sentinel.  This is done just by searching
204          * the first THRESH elements (or the first n if n < THRESH), finding
205          * the min, and swapping it into the first position.
206          */
207         for (j = lo = base; (lo += qsz) < hi; )
208                 if (qcmp(j, lo) > 0)
209                         j = lo;
210         if (j != base) {
211                 /* swap j into place */
212                 for (i = base, hi = base + qsz; i < hi; ) {
213                         c = *j;
214                         *j++ = *i;
215                         *i++ = c;
216                 }
217         }
218         /*
219          * With our sentinel in place, we now run the following hyper-fast
220          * insertion sort.  For each remaining element, min, from [1] to [n-1],
221          * set hi to the index of the element AFTER which this one goes.
222          * Then, do the standard insertion sort shift on a character at a time
223          * basis for each element in the frob.
224          */
225         for (min = base; (hi = min += qsz) < max; ) {
226                 while (qcmp(hi -= qsz, min) > 0)
227                         /* void */;
228                 if ((hi += qsz) != min) {
229                         for (lo = min + qsz; --lo >= min; ) {
230                                 c = *lo;
231                                 for (i = j = lo; (j -= qsz) >= hi; i = j)
232                                         *i = *j;
233                                 *i = c;
234                         }
235                 }
236         }
237    return(0);
238 }
239
240 //
241 // --- Allocate memory; Panic and Quit if we Run Out -----------------------------------------------
242 //
243
244 char * amem(LONG amount)
245 {
246         char * p;
247
248         if (amount & 1)                                           // Keep word alignment
249                 ++amount;
250
251         if (amount < A_THRESH)
252         {                                  // Honor *small* request
253                 if (a_amount < amount)
254                 {
255                         a_ptr = amem(A_AMOUNT);
256                         a_amount = A_AMOUNT;
257                 }
258
259                 p = a_ptr;
260                 a_ptr += amount;
261                 a_amount -= amount;
262         }
263         else
264         {
265                 amemtot += amount;                                    // Bump total alloc
266                 p = (char *)malloc(amount);                           // Get memory from malloc
267
268                 if ((LONG)p == (LONG)NULL)
269                         fatal("memory exhausted");
270
271                 memset(p, 0, amount);
272         }
273
274         return p;
275 }
276
277 //
278 // --- Copy stuff around, return pointer to dest+count+1 (doesn't handle overlap) ------------------
279 //
280
281 char * copy(char * dest, char * src, LONG count)
282 {
283    while (count--)
284       *dest++ = *src++;
285
286    return dest;
287 }
288
289 //
290 // --- Clear a region of memory --------------------------------------------------------------------
291 //
292
293 void clear(char *dest, LONG count)
294 {
295         while(count--)
296                 *dest++ = 0;
297 }
298
299 //
300 // --- Check to see if the string is a keyword. Returns -1, or a value from the 'accept[]' table ---
301 //
302
303 int kmatch(char * p, int * base, int * check, int * tab, int * accept)
304 {
305         int state;
306         int j;
307
308         for(state=0; state>=0;)
309         {
310                 j = base[state] + (int)tolowertab[*p];
311
312                 if (check[j] != state)
313                 {                               // Reject, character doesn't match
314                         state = -1;                                        // No match 
315                         break;
316                 }
317
318                 if (!*++p)
319                 {                                           // Must accept or reject at EOS
320                         state = accept[j];                                 // (-1 on no terminal match) 
321                         break;
322                 }
323
324                 state = tab[j];
325         }
326
327         return state;
328 }
329
330 //
331 // --- Auto-even a section -------------------------------------------------------------------------
332 //
333
334 void autoeven(int sect)
335 {
336         switchsect(sect);
337         d_even();
338         savsect();
339 }
340
341 //
342 // -------------------------------------------------------------------------------------------------
343 // Manipulate file extension.
344 // `name' must be large enough to hold any possible filename.
345 // If `stripp' is nonzero, any old extension is removed.
346 // Then, if the file does not already have an extension,
347 // `extension' is appended to the filename.
348 // -------------------------------------------------------------------------------------------------
349 //
350
351 char *fext(char *name, char *extension, int stripp) {
352    char *s, *beg;                                           // String pointers
353
354    // Find beginning of "real" name
355    beg = name + strlen(name) - 1;
356    for(; beg > name; --beg) {
357       if(*beg == SLASHCHAR) {
358          ++beg;
359          break;
360       }
361    }
362
363    if(stripp) {                                             // Clobber any old extension
364       for(s = beg; *s && *s != '.'; ++s) 
365          ;
366       *s = '\0';
367    }
368
369    for(s = beg; *s != '.'; ++s) {
370       if(!*s) {                                             // Append the new extension
371          strcat(beg, extension);
372          break;
373       }
374    }
375
376    return(name);
377 }
378
379 //
380 // -------------------------------------------------------------------------------------------------
381 // Return `item'nth element of semicolon-seperated pathnames specified in the enviroment string `s'.
382 // Copy the pathname to `buf'.  Return 0 if the `item'nth path doesn't exist.
383 // 
384 // [`item' ranges from 0 to N-1, where N = #elements in search path]
385 // -------------------------------------------------------------------------------------------------
386 //
387
388 int nthpath(char * env_var, int itemno, char * buf)
389 {
390         char * s = searchpath;
391
392         if (s == NULL)
393                 s = getenv(env_var);
394
395         if (s == NULL)
396                 return 0;
397
398         while (itemno--)
399                 while (*s != EOS && *s++ != ';')
400                         ;
401
402         if (*s == EOS)
403                 return 0;
404
405         while (*s != EOS && *s != ';')
406                 *buf++ = *s++;
407
408         *buf++ = EOS;
409
410         return 1;
411 }
412
413 //
414 // --- Display Command Line Help -----------------------------------------------
415 //
416
417 void display_help(void)
418 {
419         printf("Usage:\n");
420         printf("    %s [options] srcfile\n", cmdlnexec);
421         printf("\n");
422         printf("Options:\n");
423         printf("   -? or -h              display usage information\n");
424         printf("   -dsymbol[=value]      define symbol\n");
425         printf("   -e[errorfile]         send error messages to file, not stdout\n");
426         printf("   -f[format]            output object file format\n");
427         printf("                         b: BSD (use this for Jaguar)\n");
428         printf("   -i[path]              directory to search for include files\n");
429         printf("   -l[filename]          create an output listing file\n");
430         printf("   -o file               output file name\n");
431         printf("   -r[size]              pad segments to boundary size specified\n");
432         printf("                         w: word (2 bytes, default alignment)\n");
433         printf("                         l: long (4 bytes)\n");
434         printf("                         p: phrase (8 bytes)\n");
435         printf("                         d: double phrase (16 bytes)\n");
436         printf("                         q: quad phrase (32 bytes)\n");
437         printf("   -s                    warn about possible short branches\n");
438         printf("   -u                    force referenced and undefined symbols global\n");
439         printf("   -v                    set verbose mode\n");
440         printf("   -y[pagelen]           set page line length (default: 61)\n");
441         printf("\n");
442 }
443
444 //
445 // --- Display Version Information ---------------------------------------------
446 //
447
448 void display_version(void)
449 {
450         printf("\nReboot's Macro Assembler for Atari Jaguar\n"); 
451         printf("Copyright (C) 199x Landon Dyer, 2011 Reboot\n"); 
452         printf("V%01i.%01i.%01i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM);
453 }
454
455 // 
456 // --- Process Command Line Arguments and do an Assembly -------------------------------------------
457 //
458
459 int process(int argc, char **argv) {
460    int argno;                                               // Argument number
461    SYM *sy;                                                 // Pointer to a symbol record
462    char *s;                                                 // String pointer
463    int fd;                                                  // File descriptor
464    char fnbuf[FNSIZ];                                       // Filename buffer 
465    int i;                                                   // Iterator
466
467    errcnt = 0;                                              // Initialise error count
468    listing = 0;                                             // Initialise listing level
469    list_flag = 0;                                           // Initialise listing flag
470    verb_flag = perm_verb_flag;                              // Initialise verbose flag
471    as68_flag = 0;                                           // Initialise as68 kludge mode
472    glob_flag = 0;                                           // Initialise .globl flag
473    sbra_flag = 0;                                           // Initialise short branch flag
474    debug = 0;                                               // Initialise debug flag
475    searchpath = NULL;                                       // Initialise search path
476    objfname = NULL;                                         // Initialise object filename
477    list_fname = NULL;                                       // Initialise listing filename
478    err_fname = NULL;                                        // Initialise error filename
479    obj_format = BSD;                                        // Initialise object format
480    firstfname = NULL;                                       // Initialise first filename
481    err_fd = ERROUT;                                         // Initialise error file descriptor
482    err_flag = 0;                                            // Initialise error flag
483    rgpu = 0;                                                // Initialise GPU assembly flag
484    rdsp = 0;                                                // Initialise DSP assembly flag
485    lsym_flag = 1;                                           // Include local symbols in object file
486    regbank = BANK_N;                                        // No RISC register bank specified
487    orgactive = 0;                                           // Not in RISC org section
488    orgwarning = 0;                                          // No ORG warning issued
489    a_amount = 0;
490    segpadsize = 2;                                          // Initialise segment padding size
491    in_main = 0;
492
493    // Initialise modules
494         init_sym();                                              // Symbol table
495    init_token();                                            // Tokenizer
496    init_procln();                                           // Line processor
497    init_expr();                                             // Expression analyzer
498    init_sect();                                             // Section manager / code generator
499    init_mark();                                             // Mark tape-recorder
500         init_macro();                                            // Macro processor
501    init_list();                                             // Listing generator
502
503    // Process command line arguments and assemble source files
504    for(argno = 0; argno < argc; ++argno) {
505       if(*argv[argno] == '-') {
506          switch(argv[argno][1]) {
507             case 'd':                                       // Define symbol
508             case 'D':
509                for(s = argv[argno] + 2; *s != EOS;) {
510                   if(*s++ == '=') {
511                      s[-1] = EOS;
512                      break;
513                                                 }
514                }
515                if(argv[argno][2] == EOS) {
516                   printf("-d: empty symbol\n");
517                   ++errcnt;
518                   return(errcnt);
519                }
520                sy = lookup(argv[argno] + 2, 0, 0);
521                if(sy == NULL) {
522                   sy = newsym(argv[argno] + 2, LABEL, 0);
523                   sy->svalue = 0;
524                }
525                sy->sattr = DEFINED | EQUATED | ABS;
526                if(*s)
527                   sy->svalue = (VALUE)atoi(s);
528                else
529                   sy->svalue = 0;
530                break;
531             case 'e':                                       // Redirect error message output
532             case 'E':
533                err_fname = argv[argno] + 2;
534                break;
535             case 'f':                                       // -f<format>
536             case 'F':
537                switch(argv[argno][2]) {
538                   case EOS:
539                   case 'b':                                 // -fb = BSD (Jaguar Recommended)
540                   case 'B':
541                      obj_format = BSD;
542                      break;
543                   default:
544                      printf("-f: unknown object format specified\n");
545                      ++errcnt;
546                      return(errcnt);
547                }
548                break;
549             case 'g':                                       // Debugging flag
550             case 'G':
551                printf("Debugging flag (-g) not yet implemented\n");
552                break;
553             case 'i':                                       // Set directory search path
554             case 'I':
555                searchpath = argv[argno] + 2;
556                break;
557             case 'l':                                       // Produce listing file
558             case 'L':
559                list_fname = argv[argno] + 2;
560                listing = 1;
561                list_flag = 1;
562                ++lnsave;
563                break;
564             case 'o':                                       // Direct object file output
565             case 'O':
566                if(argv[argno][2] != EOS) objfname = argv[argno] + 2;
567                else {
568                   if(++argno >= argc) {
569                      printf("Missing argument to -o");
570                      ++errcnt;
571                      return(errcnt);
572                   }
573                   objfname = argv[argno];
574                }
575                break;
576             case 'r':                                       // Pad seg to requested boundary size
577             case 'R':
578                switch(argv[argno][2]) {
579                   case 'w': case 'W': segpadsize = 2;  break;  
580                   case 'l': case 'L': segpadsize = 4;  break;
581                   case 'p': case 'P': segpadsize = 8;  break;
582                   case 'd': case 'D': segpadsize = 16; break;
583                   case 'q': case 'Q': segpadsize = 32; break;
584                   default: segpadsize = 2; break;           // Effective autoeven();
585                }
586                break;
587             case 's':                                       // Warn about possible short branches
588             case 'S':
589                sbra_flag = 1;
590                break;
591             case 'u':                                       // Make undefined symbols .globl
592             case 'U':
593                glob_flag = 1;
594                break;
595             case 'v':                                       // Verbose flag
596             case 'V':
597                verb_flag++;
598                if(verb_flag > 1) display_version();
599                break;
600             case 'x':                                       // Turn on debugging
601             case 'X':
602                debug = 1;
603                printf("~ Debugging ON\n");
604                break;
605             case 'y':                                       // -y<pagelen>
606             case 'Y':
607                pagelen = atoi(argv[argno] + 2);
608                if(pagelen < 10) {
609                   printf("-y: bad page length\n");
610                   ++errcnt;
611                   return(errcnt);
612                }
613                break;
614             case EOS:                                       // Input is stdin
615                if(firstfname == NULL)                       // Kludge first filename
616                   firstfname = defname;
617                include(0, "(stdin)");
618                assemble();
619                break;
620             case 'h':                                       // Display command line usage
621             case 'H':
622             case '?':
623                display_version();
624                display_help();
625                ++errcnt;
626                break;
627             default:
628                display_version();
629                printf("Unknown switch: %s\n\n", argv[argno]);
630                display_help();
631                ++errcnt;
632                break;
633          }
634       } else {
635          // Record first filename.
636          if(firstfname == NULL)
637             firstfname = argv[argno];
638          strcpy(fnbuf, argv[argno]);
639          fext(fnbuf, ".s", 0);
640          fd = open(fnbuf, 0);
641          if(fd < 0) {
642             printf("Cannot open: %s\n", fnbuf);
643             ++errcnt;
644             continue;
645          }
646          include(fd, fnbuf);
647          assemble();
648       }
649    }
650
651    // Wind-up processing;
652    // o  save current section (no more code generation)
653    // o  do auto-even of all sections (or boundary alignment as requested through '-r')
654    // o  determine name of object file:
655    //    -  "foo.o" for linkable output;
656    //    -  "foo.prg" for GEMDOS executable (-p flag).
657    savsect();
658    for(i = TEXT; i <= BSS; i <<= 1) {
659       switchsect(i);
660       switch(segpadsize) {
661          case 2:  d_even();    break;
662          case 4:  d_long();    break;
663          case 8:  d_phrase();  break;
664          case 16: d_dphrase(); break;
665          case 32: d_qphrase(); break;
666       }
667       savsect();
668    }
669
670    if(objfname == NULL) {
671       if(firstfname == NULL)
672          firstfname = defname;
673       strcpy(fnbuf, firstfname);
674       //fext(fnbuf, prg_flag ? ".prg" : ".o", 1);
675       fext(fnbuf, ".o", 1);
676       objfname = fnbuf;
677    }
678
679    // With one pass finished, go back and:
680    // (1)   run through all the fixups and resolve forward references;
681    // (1.5) ensure that remaining fixups can be handled by the linker
682    //       (`lo68' format, extended (postfix) format....)
683    // (2)   generate the output file image and symbol table;
684    // (3)   generate relocation information from left-over fixups.
685    fixups();                                                // Do all fixups
686    stopmark();                                              // Stop mark tape-recorder
687    if(errcnt == 0) {
688       if((fd = open(objfname, _OPEN_FLAGS, _PERM_MODE)) < 0)
689          cantcreat(objfname);
690       if(verb_flag) {
691          s = "object";
692          printf("[Writing %s file: %s]\n", s, objfname);
693       }
694       object((WORD)fd);
695       close(fd);
696       if(errcnt != 0)
697          unlink(objfname);
698    }
699
700    if(list_flag) {
701       if(verb_flag) printf("[Wrapping-up listing file]\n");
702       listing = 1;
703       symtable();
704       close(list_fd);
705    }
706
707    if(err_flag)
708       close(err_fd);
709
710    DEBUG dump_everything();
711
712    return(errcnt);
713 }
714
715 //
716 // --- Interactive Mode ----------------------------------------------------------------------------
717 //
718
719 void interactive(void)
720 {
721         char * s;                                                 // String pointer for banner
722         char ln[LNSIZ];                                          // Input line
723         char * argv[MAXARGV];                                     // Argument values
724         int argcnt;                                              // Argument count
725
726    // As there is no command line, print a copyright message and prompt for command line
727         s = "*****************************************************\n";
728         printf("\n%s* RMAC - Reboot's Macro Assembler for Atari Jaguar *\n", s);
729         printf("* Copyright (C) 199x Landon Dyer, 2011 Reboot       *\n");
730         printf("* Version %01i.%01i.%01i                Platform: %-9s *\n",MAJOR,MINOR,PATCH,PLATFORM);
731         printf("* ------------------------------------------------- *\n");
732         printf("* INTERACTIVE MODE                                  *\n%s\n", s);
733
734         perm_verb_flag = 1;                                      // Enter permanent verbose mode
735
736         // Handle commandlines until EOF or we get an empty one
737         for(;;)
738         {
739                 loop:
740                 printf("* ");
741                 fflush(stdout);                                       // Make prompt visible
742
743                 if (gets(ln) == NULL || !*ln)                          // Get input line
744                         break;
745
746                 argcnt = 0;                                           // Process input line
747                 s = ln;
748
749                 while (*s)
750                 {
751                         if (isspace(*s))
752                         {                                  // Skip whitespace
753                                 ++s;
754                         }
755                         else
756                         {
757                                 if (argcnt >= MAXARGV)
758                                 {
759                                         printf("Too many arguments\n");
760                                         goto loop;
761                                 }
762
763                                 argv[argcnt++] = s;
764
765                                 while (*s && !isspace(*s))
766                                         ++s;
767
768                                 if (isspace(*s))
769                                         *s++ = EOS;
770                         }
771                 }
772
773                 if (argcnt == 0)                                       // Exit if no arguments
774                         break;
775
776                 process(argcnt, argv);                                // Process arguments
777
778                 if (errcnt)
779                         printf("%d assembly error%s\n", errcnt, (errcnt > 1) ? "s" : "");
780         }
781 }
782
783 //
784 // --- Determine Processor Endianess ---------------------------------------------------------------
785 //
786
787 int get_endianess(void)
788 {
789         int i = 1;
790         char * p = (char *)&i;
791
792         if (p[0] == 1)
793                 return 0;
794
795         return 1;
796 }
797
798 //
799 // --- Application Entry Point; Handle the Command Line --------------------------------------------
800 //
801
802 int main(int argc, char ** argv)
803 {
804         int status;                                              // Status flag
805         int i;
806
807         perm_verb_flag = 0;                                      // Clobber "permanent" verbose flag
808         cmdlnexec = argv[0];                                     // Obtain executable name
809
810         endian = get_endianess();                                // Get processor endianess
811
812         for(i=0; i<MAXFWDJUMPS; i++)
813                 fwdjump[i] = 0;
814
815         if (argc > 1)
816         {                                           // Full command line passed
817                 status = process(argc - 1, argv + 1);              
818         }
819         else
820         {                                                 // Interactive mode 
821                 status = 0;
822                 interactive();
823         }
824
825         return status;
826 }