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