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