]> Shamusworld >> Repos - rmac/blob - rmac.c
Fixed a wrong optimisation switch test, corrected some messages, updated docs
[rmac] / rmac.c
1 //
2 // RMAC - Renamed Macro Assembler for all Atari computers
3 // RMAC.C - Main Application Code
4 // Copyright (C) 199x Landon Dyer, 2011-2021 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 "6502.h"
11 #include "debug.h"
12 #include "direct.h"
13 #include "dsp56k.h"
14 #include "error.h"
15 #include "expr.h"
16 #include "listing.h"
17 #include "mark.h"
18 #include "macro.h"
19 #include "object.h"
20 #include "procln.h"
21 #include "riscasm.h"
22 #include "sect.h"
23 #include "symbol.h"
24 #include "token.h"
25 #include "version.h"
26
27 int perm_verb_flag;                             // Permanently verbose, interactive mode
28 int list_flag;                                  // "-l" listing flag on command line
29 int list_pag = 1;                               // Enable listing pagination by default
30 int verb_flag;                                  // Be verbose about what's going on
31 int m6502;                                              // 1, assembling 6502 code
32 int glob_flag;                                  // Assume undefined symbols are global
33 int lsym_flag;                                  // Include local symbols in object file
34 int optim_warn_flag;                    // Warn about possible short branches
35 int prg_flag;                                   // !=0, produce .PRG executable (2=symbols)
36 int prg_extend;                                 // !=0, output extended .PRG symbols
37 int legacy_flag;                                // Do stuff like insert code in RISC assembler
38 int obj_format;                                 // Object format flag
39 int debug;                                              // [1..9] Enable debugging levels
40 int err_flag;                                   // '-e' specified
41 int err_fd;                                             // File to write error messages to
42 int rgpu, rdsp;                                 // Assembling Jaguar GPU or DSP code
43 int robjproc;                                   // Assembling Jaguar Object Processor code
44 int dsp56001;                                   // Assembling DSP 56001 code
45 int list_fd;                                    // File to write listing to
46 int regbank;                                    // RISC register bank
47 int segpadsize;                                 // Segment padding size
48 int endian;                                             // Host processor endianess (0 = LE, 1 = BE)
49 char * objfname;                                // Object filename pointer
50 char * firstfname;                              // First source filename
51 char * cmdlnexec;                               // Executable name, pointer to ARGV[0]
52 char searchpatha[512] = { 0 };  // Buffer to hold searchpath when specified
53 char * searchpath = NULL;               // Search path for include files
54 char defname[] = "noname.o";    // Default output filename
55 int optim_flags[OPT_COUNT_ALL] = { 0 }; // Specific optimisations on/off matrix
56 int activecpu = CPU_68000;              // Active 68k CPU (68000 by default)
57 int activefpu = FPU_NONE;               // Active FPU (none by default)
58 int org68k_active = 0;                  // .org switch for 68k (only with RAW output format)
59 uint32_t org68k_address;                // .org for 68k
60
61 //
62 // Convert a string to uppercase
63 //
64 void strtoupper(char * s)
65 {
66         while (*s)
67                 *s++ &= 0xDF;
68 }
69
70 //
71 // Manipulate file extension.
72 //
73 // 'name' must be large enough to hold any possible filename. If 'stripp' is
74 // nonzero, any old extension is removed. If the file does not already have an
75 // extension, 'extension' is appended to the filename.
76 //
77 char * fext(char * name, char * extension, int stripp)
78 {
79         char * s;
80
81         // Find beginning of "real" name (strip off path)
82         char * beg = strrchr(name, SLASHCHAR);
83
84         if (beg == NULL)
85                 beg = name;
86
87         // Clobber any old extension, if requested
88         if (stripp)
89         {
90                 for(s=beg; *s && *s!='.'; ++s)
91                         ;
92
93                 *s = '\0';
94         }
95
96         if (strrchr(beg, '.') == NULL)
97                 strcat(beg, extension);
98
99         return name;
100 }
101
102 static int is_sep(char c)
103 {
104     const char *seps = PATH_SEPS;
105
106     for (seps = PATH_SEPS; *seps; seps++) {
107         if (*seps == c)
108             return 1;
109     }
110
111     return 0;
112 }
113
114 //
115 // Return 'item'nth element of semicolon-seperated pathnames specified in the
116 // enviroment string 's'. Copy the pathname to 'buf'.  Return 0 if the 'item'
117 // nth path doesn't exist.
118 //
119 // ['item' ranges from 0 to N-1, where N = #elements in search path]
120 //
121 int nthpath(char * env_var, int itemno, char * buf)
122 {
123         char * s = searchpath;
124
125         if (s == NULL)
126                 s = getenv(env_var);
127
128         if (s == NULL)
129                 return 0;
130
131         while (itemno--)
132                 while (*s != EOS && !is_sep(*s++))
133                         ;
134
135         if (*s == EOS)
136                 return 0;
137
138         while (*s != EOS && !is_sep(*s))
139                 *buf++ = *s++;
140
141         *buf++ = EOS;
142
143         return 1;
144 }
145
146 //
147 // Display command line help
148 //
149 void DisplayHelp(void)
150 {
151         printf("Usage:\n"
152                 "    %s [options] srcfile\n"
153                 "\n"
154                 "Options:\n"
155                 "  -? or -h          Display usage information\n"
156                 "  -dsymbol[=value]  Define symbol (with optional value, default=0)\n"
157                 "  -e[errorfile]     Send error messages to file, not stdout\n"
158                 "  -f[format]        Output object file format\n"
159                 "                    a: ALCYON\n"
160                 "                    b: BSD (use this for Jaguar)\n"
161                 "                    e: ELF\n"
162                 "                    p: P56 (use this for DSP56001 only)\n"
163                 "                    l: LOD (use this for DSP56001 only)\n"
164                 "                    x: com/exe/xex (Atari 800)\n"
165                 "                    r: absolute address\n"
166                 "  -i[path]          Directory to search for include files\n"
167                 "  -l[filename]      Create an output listing file\n"
168                 "  -l*[filename]     Create an output listing file without pagination\n"
169                 "  -m[cpu]           Select default CPU. Available options:\n"
170                 "                    68000, 68020, 68030, 68040, 68060, 6502, tom, jerry, 56001\n"
171                 "  -n                Don't do things behind your back in RISC assembler\n"
172                 "  -o file           Output file name\n"
173                 "  +o[value]         Turn a specific optimisation on\n"
174                 "                    Available optimisation switches:\n"
175                 "                    o0: Absolute long addresses to word\n"
176                 "                    o1: move.l #x,dn/an to moveq\n"
177                 "                    o2: Word branches to short\n"
178                 "                    o3: Outer displacement 0(an) to (an)\n"
179                 "                    o4: lea size(An),An to addq #size,An\n"
180                 "                    o5: 68020+ Absolute long base/outer disp. to word\n"
181                 "                    o6: Null branches to NOP\n"
182                 "                    o7: clr.l Dx to moveq #0,Dx\n"
183                 "                    o8: adda.w/l #x,Dy to addq.w/l #x,Dy\n"
184                 "                    o9: adda.w/l #x,Ay to lea x(Dy),Ay\n"
185                 "                    o10: 56001 Use short format for immediate values if possible\n"
186                 "                    o11: 56001 Auto convert short addressing mode to long (default: on)\n"
187                 "                    o30: Enforce PC relative (alternative name: op)\n"
188                 "  ~o[value]         Turn a specific optimisation off\n"
189                 "  +oall             Turn all optimisations on\n"
190                 "  ~oall             Turn all optimisations off\n"
191                 "  -p                Create an ST .prg (without symbols). Forces -fa\n"
192                 "  -ps               Create an ST .prg (with symbols). Forces -fa\n"
193                 "  -px               Create an ST .prg (with exsymbols). Forces -fa\n"
194                 "  -r[size]          Pad segments to boundary size specified\n"
195                 "                    w: word (2 bytes, default alignment)\n"
196                 "                    l: long (4 bytes)\n"
197                 "                    p: phrase (8 bytes)\n"
198                 "                    d: double phrase (16 bytes)\n"
199                 "                    q: quad phrase (32 bytes)\n"
200                 "  -s                Warn about possible short branches\n"
201                 "                    and applied optimisations\n"
202                 "  -u                Force referenced and undefined symbols global\n"
203                 "  -v                Set verbose mode\n"
204                 "  -x                Turn on debugging mode\n"
205                 "  -y[pagelen]       Set page line length (default: 61)\n"
206                 "\n", cmdlnexec);
207 }
208
209
210 //
211 // Display version information
212 //
213 void DisplayVersion(void)
214 {
215         printf("\n"
216                 " _ __ _ __ ___   __ _  ___ \n"
217                 "| '__| '_ ` _ \\ / _` |/ __|\n"
218                 "| |  | | | | | | (_| | (__ \n"
219                 "|_|  |_| |_| |_|\\__,_|\\___|\n"
220                 "\nRenamed Macro Assembler\n"
221                 "Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends\n"
222                 "V%01i.%01i.%01i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM);
223 }
224
225 //
226 // Parse optimisation options
227 //
228 int ParseOptimization(char * optstring)
229 {
230         int onoff = 0;
231
232         while (*optstring)
233         {
234                 if (*optstring == '+')
235                         onoff = 1;
236                 else if (*optstring != '~')
237                         return ERROR;
238
239         if (optstring[2] == 0)
240             return error(".opt called with zero arguments");
241
242                 if ((optstring[2] == 'a' || optstring[2] == 'A')
243                         && (optstring[3] == 'l' || optstring[3] == 'L')
244                         && (optstring[4] == 'l' || optstring[4] == 'L'))
245                 {
246                         memset(optim_flags, onoff, OPT_COUNT * sizeof(int));
247                         optstring += 5;
248                 }
249                 else if (optstring[1] == 'o' || optstring[1] == 'O') // Turn on specific optimisation
250                 {
251                         if (optstring[2] == 'p' || optstring[2] == 'P')
252                         {
253                                 optim_flags[OPT_PC_RELATIVE] = onoff;
254                                 optstring += 3;
255                         }
256                         else
257                         {
258                                 int opt_no = atoi(&optstring[2]);
259
260                                 if ((opt_no >= 0) && (opt_no < OPT_COUNT))
261                                 {
262                                         optim_flags[opt_no] = onoff;
263                                         optstring += 3;
264
265                                         // If opt_no is 2 digits then consume an extra character.
266                                         // Sounds a bit sleazy but we know we're not going to hit
267                                         // more than 100 optimisation switches so this should be
268                                         // fine. If we do hit that number then it'll probably be
269                                         // time to burn the whole codebase and start from scratch.
270                                         if (opt_no > 9)
271                                                 optstring++;
272                                 }
273                                 else
274                                         return ERROR;
275                         }
276                 }
277                 else
278                 {
279                         return ERROR;
280                 }
281
282                 if (*optstring == ',')
283                         optstring++;
284         }
285         return OK;
286 }
287
288 //
289 // Process command line arguments and do an assembly
290 //
291 int Process(int argc, char ** argv)
292 {
293         int argno;                                              // Argument number
294         SYM * sy;                                               // Pointer to a symbol record
295         char * s;                                               // String pointer
296         int fd;                                                 // File descriptor
297         char fnbuf[FNSIZ];                              // Filename buffer
298         int i;                                                  // Iterator
299         int current_path_index = 0;             // Iterator for search paths
300
301         errcnt = 0;                                             // Initialize error count
302         listing = 0;                                    // Initialize listing level
303         list_flag = 0;                                  // Initialize listing flag
304         verb_flag = perm_verb_flag;             // Initialize verbose flag
305         glob_flag = 0;                                  // Initialize .globl flag
306         optim_warn_flag = 0;                    // Initialize short branch flag
307         debug = 0;                                              // Initialize debug flag
308         objfname = NULL;                                // Initialize object filename
309         list_fname = NULL;                              // Initialize listing filename
310         err_fname = NULL;                               // Initialize error filename
311         obj_format = BSD;                               // Initialize object format
312         firstfname = NULL;                              // Initialize first filename
313         err_fd = ERROUT;                                // Initialize error file descriptor
314         err_flag = 0;                                   // Initialize error flag
315         rgpu = 0;                                               // Initialize GPU assembly flag
316         rdsp = 0;                                               // Initialize DSP assembly flag
317         robjproc = 0;                                   // Initialize OP assembly flag
318         lsym_flag = 1;                                  // Include local symbols in object file
319         regbank = BANK_N;                               // No RISC register bank specified
320         orgactive = 0;                                  // Not in RISC org section
321         orgwarning = 0;                                 // No ORG warning issued
322         segpadsize = 2;                                 // Initialize segment padding size
323     dsp_orgmap[0].start = 0;            // Initialize 56001 org initial address
324     dsp_orgmap[0].memtype = ORG_P;      // Initialize 56001 org start segment
325         m6502 = 0;                                              // 6502 mode off by default
326
327         // Initialize modules
328         InitSymbolTable();                              // Symbol table
329         InitTokenizer();                                // Tokenizer
330         InitLineProcessor();                    // Line processor
331         InitExpression();                               // Expression analyzer
332         InitSection();                                  // Section manager / code generator
333         InitMark();                                             // Mark tape-recorder
334         InitMacro();                                    // Macro processor
335         InitListing();                                  // Listing generator
336         Init6502();                                             // 6502 assembler
337
338         // Process command line arguments and assemble source files
339         for(argno=0; argno<argc; argno++)
340         {
341                 if (*argv[argno] == '-')
342                 {
343                         switch (argv[argno][1])
344                         {
345                         case 'd':                               // Define symbol
346                         case 'D':
347                                 for(s=argv[argno]+2; *s!=EOS;)
348                                 {
349                                         if (*s++ == '=')
350                                         {
351                                                 s[-1] = EOS;
352                                                 break;
353                                         }
354                                 }
355
356                                 if (argv[argno][2] == EOS)
357                                 {
358                                         printf("-d: empty symbol\n");
359                                         errcnt++;
360                                         return errcnt;
361                                 }
362
363                                 sy = lookup(argv[argno] + 2, 0, 0);
364
365                                 if (sy == NULL)
366                                 {
367                                         sy = NewSymbol(argv[argno] + 2, LABEL, 0);
368                                         sy->svalue = 0;
369                                 }
370
371                                 sy->sattr = DEFINED | EQUATED | ABS;
372                                 sy->svalue = (*s ? (uint64_t)atoi(s) : 0);
373                                 break;
374                         case 'e':                               // Redirect error message output
375                         case 'E':
376                                 err_fname = argv[argno] + 2;
377                                 break;
378                         case 'f':                               // -f<format>
379                         case 'F':
380                                 switch (argv[argno][2])
381                                 {
382                                 case EOS:
383                                 case 'a':                       // -fa = Alcyon [the default]
384                                 case 'A':
385                                         obj_format = ALCYON;
386                                         break;
387                                 case 'b':                       // -fb = BSD (Jaguar Recommended: 3 out 4 jaguars recommend it!)
388                                 case 'B':
389                                         obj_format = BSD;
390                                         break;
391                                 case 'e':                       // -fe = ELF
392                                 case 'E':
393                                         obj_format = ELF;
394                                         break;
395                 case 'l':           // -fl = LOD
396                 case 'L':
397                     obj_format = LOD;
398                     break;
399                 case 'p':           // -fp = P56
400                 case 'P':
401                     obj_format = P56;
402                                         break;
403                                 case 'x':                       // -fx = COM/EXE/XEX
404                                 case 'X':
405                                         obj_format = XEX;
406                                         break;
407                 case 'r':           // -fr = Absolute address
408                 case 'R':
409                     obj_format = RAW;
410                     break;
411                                 default:
412                                         printf("-f: unknown object format specified\n");
413                                         errcnt++;
414                                         return errcnt;
415                                 }
416                                 break;
417                         case 'g':                               // Debugging flag
418                         case 'G':
419                                 printf("Debugging flag (-g) not yet implemented\n");
420                                 break;
421                         case 'i':                               // Set directory search path
422                         case 'I':
423                         {
424                                 strcat(searchpatha, argv[argno] + 2);
425                                 strcat(searchpatha, ";");
426                                 searchpath = searchpatha;
427
428                                 // Check to see if include paths actually exist
429                                 char current_path[256];
430
431                                 for(i=current_path_index; nthpath("RMACPATH", i, current_path)!=0; i++)
432                                 {
433                                         if (strlen(current_path) > 0)
434                                         {
435                                                 DIR * test = opendir(current_path);
436
437                                                 if (test == NULL)
438                                                 {
439                                                         printf("Invalid include path: %s\n", current_path);
440                                                         errcnt++;
441                                                         return errcnt;
442                                                 }
443
444                                                 closedir(test);
445                                         }
446                                 }
447
448                                 current_path_index = i - 1;
449                                 break;
450                         }
451                         case 'l':                               // Produce listing file
452                         case 'L':
453                                 if (*(argv[argno] + 2) == '*')
454                                 {
455                                         list_fname = argv[argno] + 3;
456                                         list_pag = 0;    // Special case - turn off pagination
457                                 }
458                                 else
459                                 {
460                                         list_fname = argv[argno] + 2;
461                                 }
462
463                                 listing = 1;
464                                 list_flag = 1;
465                                 lnsave++;
466                                 break;
467                         case 'm':
468                         case 'M':
469                                 if (strcmp(argv[argno] + 2, "68000") == 0)
470                                         d_68000();
471                                 else if (strcmp(argv[argno] + 2, "68020") == 0)
472                                         d_68020();
473                                 else if (strcmp(argv[argno] + 2, "68030") == 0)
474                                         d_68030();
475                                 else if (strcmp(argv[argno] + 2, "68040") == 0)
476                                         d_68040();
477                                 else if (strcmp(argv[argno] + 2, "68060") == 0)
478                                         d_68060();
479                                 else if (strcmp(argv[argno] + 2, "68881") == 0)
480                                         d_68881();
481                                 else if (strcmp(argv[argno] + 2, "68882") == 0)
482                                         d_68882();
483                                 else if (strcmp(argv[argno] + 2, "56001") == 0)
484                                         d_56001();
485                                 else if (strcmp(argv[argno] + 2, "6502") == 0)
486                                         d_6502();
487                                 else if (strcmp(argv[argno] + 2, "tom") == 0)
488                                         d_gpu();
489                                 else if (strcmp(argv[argno] + 2, "jerry") == 0)
490                                         d_dsp();
491                                 else
492                                 {
493                                         printf("Unrecognized CPU '%s'\n", argv[argno] + 2);
494                                         errcnt++;
495                                         return errcnt;
496                                 }
497                                 break;
498                         case 'o':                               // Direct object file output
499                         case 'O':
500                                 if (argv[argno][2] != EOS)
501                                         objfname = argv[argno] + 2;
502                                 else
503                                 {
504                                         if (++argno >= argc)
505                                         {
506                                                 printf("Missing argument to -o");
507                                                 errcnt++;
508                                                 return errcnt;
509                                         }
510
511                                         objfname = argv[argno];
512                                 }
513
514                                 break;
515                         case 'p':               /* -p: generate ".PRG" executable output */
516                         case 'P':
517                                 /*
518                                  * -p           .PRG generation w/o symbols
519                                  * -ps          .PRG generation with symbols
520                                  * -px          .PRG generation with extended symbols
521                                  */
522                                 switch (argv[argno][2])
523                                 {
524                                         case EOS:
525                                                 prg_flag = 1;
526                                                 break;
527
528                                         case 's':
529                                         case 'S':
530                                                 prg_flag = 2;
531                                                 break;
532
533                                         case 'x':
534                                         case 'X':
535                                                 prg_flag = 3;
536                                                 break;
537
538                                         default:
539                                                 printf("-p: syntax error\n");
540                                                 errcnt++;
541                                                 return errcnt;
542                                 }
543
544                                 // Enforce Alcyon object format - kind of silly
545                                 // to ask for .prg output without it!
546                                 obj_format = ALCYON;
547                                 break;
548                         case 'r':                               // Pad seg to requested boundary size
549                         case 'R':
550                                 switch(argv[argno][2])
551                                 {
552                                 case 'w': case 'W': segpadsize = 2;  break;
553                                 case 'l': case 'L': segpadsize = 4;  break;
554                                 case 'p': case 'P': segpadsize = 8;  break;
555                                 case 'd': case 'D': segpadsize = 16; break;
556                                 case 'q': case 'Q': segpadsize = 32; break;
557                                 default: segpadsize = 2; break; // Effective autoeven();
558                                 }
559                                 break;
560                         case 's':                               // Warn about possible short branches and applied optimisations
561                         case 'S':
562                                 optim_warn_flag = 1;
563                                 break;
564                         case 'u':                               // Make undefined symbols .globl
565                         case 'U':
566                                 glob_flag = 1;
567                                 break;
568                         case 'v':                               // Verbose flag
569                         case 'V':
570                                 verb_flag++;
571
572                                 if (verb_flag > 1)
573                                         DisplayVersion();
574
575                                 break;
576                         case 'x':                               // Turn on debugging
577                         case 'X':
578                                 debug = 1;
579                                 printf("~ Debugging ON\n");
580                                 break;
581                         case 'y':                               // -y<pagelen>
582                         case 'Y':
583                                 pagelen = atoi(argv[argno] + 2);
584
585                                 if (pagelen < 10)
586                                 {
587                                         printf("-y: bad page length\n");
588                                         errcnt++;
589                                         return errcnt;
590                                 }
591
592                                 break;
593                         case EOS:                               // Input is stdin
594                                 if (firstfname == NULL) // Kludge first filename
595                                         firstfname = defname;
596
597                                 include(0, "(stdin)");
598                                 Assemble();
599                                 break;
600                         case 'h':                               // Display command line usage
601                         case 'H':
602                         case '?':
603                                 DisplayVersion();
604                                 DisplayHelp();
605                                 errcnt++;
606                                 break;
607                         case 'n':                               // Turn off legacy mode
608                         case 'N':
609                                 legacy_flag = 0;
610                                 printf("Legacy mode OFF\n");
611                                 break;
612                         default:
613                                 DisplayVersion();
614                                 printf("Unknown switch: %s\n\n", argv[argno]);
615                                 DisplayHelp();
616                                 errcnt++;
617                                 break;
618                         }
619                 }
620                 else if (*argv[argno] == '+' || *argv[argno] == '~')
621                 {
622                         if (ParseOptimization(argv[argno]) != OK)
623                         {
624                                 DisplayVersion();
625                                 printf("Unknown switch: %s\n\n", argv[argno]);
626                                 DisplayHelp();
627                                 errcnt++;
628                                 break;
629                         }
630                 }
631                 else
632                 {
633                         // Record first filename.
634                         if (firstfname == NULL)
635                                 firstfname = argv[argno];
636
637                         strcpy(fnbuf, argv[argno]);
638                         fext(fnbuf, ".s", 0);
639                         fd = open(fnbuf, 0);
640
641                         if (fd < 0)
642                         {
643                                 printf("Cannot open: %s\n", fnbuf);
644                                 errcnt++;
645                                 continue;
646                         }
647
648                         include(fd, fnbuf);
649                         Assemble();
650                 }
651         }
652
653         // Wind-up processing;
654         // o  save current section (no more code generation)
655         // o  do auto-even of all sections (or boundary alignment as requested
656         //    through '-r')
657     // o  save the last pc value for 6502 (if we used 6502 at all) and
658     //    detemine if the last section contains anything
659         // o  determine name of object file:
660         //    -  "foo.o" for linkable output;
661         //    -  "foo.prg" for GEMDOS executable (-p flag).
662         SaveSection();
663         int temp_section = cursect;
664
665         for(i=TEXT; i<=BSS; i<<=1)
666         {
667                 SwitchSection(i);
668
669                 switch(segpadsize)
670                 {
671                 case 2:  d_even();    break;
672                 case 4:  d_long();    break;
673                 case 8:  d_phrase();  break;
674                 case 16: d_dphrase(); break;
675                 case 32: d_qphrase(); break;
676                 }
677
678                 SaveSection();
679         }
680
681         SwitchSection(M6502);
682
683         if (sloc != currentorg[0])
684         {
685                 currentorg[1] = sloc;
686                 currentorg += 2;
687         }
688
689         // This looks like an awful kludge...  !!! FIX !!!
690         if (temp_section & (M56001P | M56001X | M56001Y))
691         {
692                 SwitchSection(temp_section);
693
694                 if (chptr != dsp_currentorg->start)
695                 {
696                         dsp_currentorg->end = chptr;
697                         dsp_currentorg++;
698                 }
699         }
700
701         SwitchSection(TEXT);
702
703         if (objfname == NULL)
704         {
705                 if (firstfname == NULL)
706                         firstfname = defname;
707
708                 strcpy(fnbuf, firstfname);
709                 fext(fnbuf, (prg_flag ? ".prg" : ".o"), 1);
710                 objfname = fnbuf;
711         }
712
713         // With one pass finished, go back and:
714         // (1)   run through all the fixups and resolve forward references;
715         // (1.5) ensure that remaining fixups can be handled by the linker
716         //       (`lo68' format, extended (postfix) format....)
717         // (2)   generate the output file image and symbol table;
718         // (3)   generate relocation information from left-over fixups.
719         ResolveAllFixups();                                             // Do all fixups
720         StopMark();                                                             // Stop mark tape-recorder
721
722         if (errcnt == 0)
723         {
724                 if ((fd = open(objfname, _OPEN_FLAGS, _PERM_MODE)) < 0)
725                         CantCreateFile(objfname);
726
727                 if (verb_flag)
728                 {
729                         s = (prg_flag ? "executable" : "object");
730                         printf("[Writing %s file: %s]\n", s, objfname);
731                 }
732
733                 WriteObject(fd);
734                 close(fd);
735
736                 if (errcnt != 0)
737                         unlink(objfname);
738         }
739
740         if (list_flag)
741         {
742                 if (verb_flag)
743                         printf("[Wrapping-up listing file]\n");
744
745                 listing = 1;
746                 symtable();
747                 close(list_fd);
748         }
749
750         if (err_flag)
751                 close(err_fd);
752
753         DEBUG dump_everything();
754
755         return errcnt;
756 }
757
758 //
759 // Determine processor endianess
760 //
761 int GetEndianess(void)
762 {
763         int i = 1;
764         char * p = (char *)&i;
765
766         if (p[0] == 1)
767                 return 0;
768
769         return 1;
770 }
771
772 //
773 // Application entry point
774 //
775 int main(int argc, char ** argv)
776 {
777         perm_verb_flag = 0;                             // Clobber "permanent" verbose flag
778         legacy_flag = 1;                                // Default is legacy mode on (:-P)
779         optim_flags[OPT_56K_SHORT] = 1; // This ensures compatibilty with Motorola's 56k assembler
780
781         cmdlnexec = argv[0];                    // Obtain executable name
782         endian = GetEndianess();                // Get processor endianess
783
784         // If commands were passed in, process them
785         if (argc > 1)
786                 return Process(argc - 1, argv + 1);
787
788         DisplayVersion();
789         DisplayHelp();
790
791         return 0;
792 }