a24b61473ccdb2085213c0e39ec4b1b8fd3a9c62
[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 - 2016 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 "error.h"
14 #include "expr.h"
15 #include "listing.h"
16 #include "mark.h"
17 #include "macro.h"
18 #include "object.h"
19 #include "procln.h"
20 #include "riscasm.h"
21 #include "sect.h"
22 #include "symbol.h"
23 #include "token.h"
24 #include "version.h"
25
26 int perm_verb_flag;                             // Permanently verbose, interactive mode
27 int list_flag;                                  // "-l" listing flag on command line
28 int list_pag = 1;                               // Enable listing pagination by default
29 int verb_flag;                                  // Be verbose about what's going on
30 int m6502;                                              // 1, assembling 6502 code
31 int as68_flag;                                  // as68 kludge mode
32 int glob_flag;                                  // Assume undefined symbols are global
33 int lsym_flag;                                  // Include local symbols in object file
34 int sbra_flag;                                  // Warn about possible short branches
35 int prg_flag;                                   // !=0, produce .PRG executable (2=symbols)
36 int legacy_flag;                                // Do stuff like insert code in RISC assembler
37 int obj_format;                                 // Object format flag
38 int debug;                                              // [1..9] Enable debugging levels
39 int err_flag;                                   // '-e' specified
40 int err_fd;                                             // File to write error messages to
41 int rgpu, rdsp;                                 // Assembling Jaguar GPU or DSP code
42 int dsp56001;                                   // Assembling DSP 56001 code
43 int list_fd;                                    // File to write listing to
44 int regbank;                                    // RISC register bank
45 int segpadsize;                                 // Segment padding size
46 int endian;                                             // Host processor endianess (0 = LE, 1 = BE)
47 char * objfname;                                // Object filename pointer
48 char * firstfname;                              // First source filename
49 char * cmdlnexec;                               // Executable name, pointer to ARGV[0]
50 char * searchpath;                              // Search path for include files
51 char defname[] = "noname.o";    // Default output filename
52 int optim_flags[OPT_COUNT];     // Specific optimisations on/off matrix
53 int activecpu=CPU_68000;                // Active 68k CPU (68000 by default) 
54 int activefpu=FPU_NONE;                 // Active FPU (none by default)
55
56 //
57 // Manipulate file extension.
58 //
59 // 'name' must be large enough to hold any possible filename. If 'stripp' is
60 // nonzero, any old extension is removed. If the file does not already have an
61 // extension, 'extension' is appended to the filename.
62 //
63 char * fext(char * name, char * extension, int stripp)
64 {
65         char * s;
66
67         // Find beginning of "real" name (strip off path)
68         char * beg = strrchr(name, SLASHCHAR);
69
70         if (beg == NULL)
71                 beg = name;
72
73         // Clobber any old extension, if requested
74         if (stripp)
75         {
76                 for(s=beg; *s && *s!='.'; ++s)
77                         ;
78
79                 *s = '\0';
80         }
81
82         if (strrchr(beg, '.') == NULL)
83                 strcat(beg, extension);
84
85         return name;
86 }
87
88
89 //
90 // Return 'item'nth element of semicolon-seperated pathnames specified in the
91 // enviroment string 's'. Copy the pathname to 'buf'.  Return 0 if the 'item'
92 // nth path doesn't exist.
93 //
94 // ['item' ranges from 0 to N-1, where N = #elements in search path]
95 //
96 int nthpath(char * env_var, int itemno, char * buf)
97 {
98         char * s = searchpath;
99
100         if (s == NULL)
101                 s = getenv(env_var);
102
103         if (s == NULL)
104                 return 0;
105
106         while (itemno--)
107                 while (*s != EOS && *s++ != ';')
108                         ;
109
110         if (*s == EOS)
111                 return 0;
112
113         while (*s != EOS && *s != ';')
114                 *buf++ = *s++;
115
116         *buf++ = EOS;
117
118         return 1;
119 }
120
121
122 //
123 // Display command line help
124 //
125 void DisplayHelp(void)
126 {
127         printf("Usage:\n"
128                 "    %s [options] srcfile\n"
129                 "\n"
130                 "Options:\n"
131                 "  -? or -h          Display usage information\n"
132                 "  -dsymbol[=value]  Define symbol\n"
133                 "  -e[errorfile]     Send error messages to file, not stdout\n"
134                 "  -f[format]        Output object file format\n"
135                 "                    a: ALCYON (use this for ST)\n"
136                 "                    b: BSD (use this for Jaguar)\n"
137                 "                    e: ELF\n"
138                 "                    x: com/exe/xex (Atari 800)\n"
139                 "  -i[path]          Directory to search for include files\n"
140                 "  -l[filename]      Create an output listing file\n"
141                 "  -l*[filename]     Create an output listing file without pagination\n"
142                 "  -n                Don't do things behind your back in RISC assembler\n"
143                 "  -o file           Output file name\n"
144                 "  +o[value]         Turn a specific optimisation on\n"
145                 "                    Available optimisation values and default settings:\n"
146                 "                    o0: Absolute long adddresses to word (on)\n"
147                 "                    o1: move.l #x,dn/an to moveq         (on)\n"
148                 "                    o2: Word branches to short           (on)\n"
149                 "                    o3: Outer displacement 0(an) to (an) (off)\n"
150         "                    o4: lea size(An),An to addq #size,An        (off)\n"
151         "                    o5: Absolute long base displacement to word (off)\n"
152                 "  ~o[value]         Turn a specific optimisation off\n"
153                 "  +oall             Turn all optimisations on\n"
154                 "  ~oall             Turn all optimisations off\n"
155                 "  -p                Create an ST .prg (without symbols)\n"
156                 "  -ps               Create an ST .prg (with symbols)\n"
157                 "                    Forces -fa\n"
158                 "  -r[size]          Pad segments to boundary size specified\n"
159                 "                    w: word (2 bytes, default alignment)\n"
160                 "                    l: long (4 bytes)\n"
161                 "                    p: phrase (8 bytes)\n"
162                 "                    d: double phrase (16 bytes)\n"
163                 "                    q: quad phrase (32 bytes)\n"
164                 "  -s                Warn about possible short branches\n"
165                 "                    and applied optimisations\n"
166                 "  -u                Force referenced and undefined symbols global\n"
167                 "  -v                Set verbose mode\n"
168                 "  -x                Turn on debugging mode\n"
169                 "  -y[pagelen]       Set page line length (default: 61)\n"
170                 "\n", cmdlnexec);
171 }
172
173
174 //
175 // Display version information
176 //
177 void DisplayVersion(void)
178 {
179         printf("\n"
180                 " _ __ _ __ ___   __ _  ___ \n"
181                 "| '__| '_ ` _ \\ / _` |/ __|\n"
182                 "| |  | | | | | | (_| | (__ \n"
183                 "|_|  |_| |_| |_|\\__,_|\\___|\n"
184                 "\nReboot's Macro Assembler\n"
185                 "Copyright (C) 199x Landon Dyer, 2011-2017 Reboot\n"
186                 "V%01i.%01i.%01i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM);
187 }
188
189
190 //
191 // Parse optimisation options
192 //
193 int ParseOptimization(char * optstring)
194 {
195         int onoff = 0;
196
197         if (*optstring == '+')
198                 onoff = 1;
199         else if (*optstring != '~')
200                 return ERROR;
201
202         if ((optstring[2] == 'a' || optstring[2] == 'A')
203                 && (optstring[3] == 'l' || optstring[3] == 'L')
204                 && (optstring[4] == 'l' || optstring[4] == 'L'))
205         {
206                 memset(optim_flags, onoff, OPT_COUNT * sizeof(int));
207                 return OK;
208         }
209         else if (optstring[1] == 'o' || optstring[1] == 'O') // Turn on specific optimisation
210         {
211                 int opt_no = atoi(&optstring[2]);
212
213                 if ((opt_no >= 0) && (opt_no < OPT_COUNT))
214                 {
215                         optim_flags[opt_no] = onoff;
216                         return OK;
217                 }
218                 else
219                         return ERROR;
220         }
221         else
222         {
223                 return ERROR;
224         }
225 }
226
227
228 //
229 // Process command line arguments and do an assembly
230 //
231 int Process(int argc, char ** argv)
232 {
233         int argno;                                              // Argument number
234         SYM * sy;                                               // Pointer to a symbol record
235         char * s;                                               // String pointer
236         int fd;                                                 // File descriptor
237         char fnbuf[FNSIZ];                              // Filename buffer
238         int i;                                                  // Iterator
239
240         errcnt = 0;                                             // Initialise error count
241         listing = 0;                                    // Initialise listing level
242         list_flag = 0;                                  // Initialise listing flag
243         verb_flag = perm_verb_flag;             // Initialise verbose flag
244         as68_flag = 0;                                  // Initialise as68 kludge mode
245         glob_flag = 0;                                  // Initialise .globl flag
246         sbra_flag = 0;                                  // Initialise short branch flag
247         debug = 0;                                              // Initialise debug flag
248         searchpath = NULL;                              // Initialise search path
249         objfname = NULL;                                // Initialise object filename
250         list_fname = NULL;                              // Initialise listing filename
251         err_fname = NULL;                               // Initialise error filename
252         obj_format = BSD;                               // Initialise object format
253         firstfname = NULL;                              // Initialise first filename
254         err_fd = ERROUT;                                // Initialise error file descriptor
255         err_flag = 0;                                   // Initialise error flag
256         rgpu = 0;                                               // Initialise GPU assembly flag
257         rdsp = 0;                                               // Initialise DSP assembly flag
258         lsym_flag = 1;                                  // Include local symbols in object file
259         regbank = BANK_N;                               // No RISC register bank specified
260         orgactive = 0;                                  // Not in RISC org section
261         orgwarning = 0;                                 // No ORG warning issued
262         segpadsize = 2;                                 // Initialise segment padding size
263         m6502 = 0;                      // 6502 mode off by default
264
265         // Initialise modules
266         InitSymbolTable();                              // Symbol table
267         InitTokenizer();                                // Tokenizer
268         InitLineProcessor();                    // Line processor
269         InitExpression();                               // Expression analyzer
270         InitSection();                                  // Section manager / code generator
271         InitMark();                                             // Mark tape-recorder
272         InitMacro();                                    // Macro processor
273         InitListing();                                  // Listing generator
274         Init6502();                                             // 6502 assembler
275
276         // Process command line arguments and assemble source files
277         for(argno=0; argno<argc; argno++)
278         {
279                 if (*argv[argno] == '-')
280                 {
281                         switch (argv[argno][1])
282                         {
283                         case 'd':                               // Define symbol
284                         case 'D':
285                                 for(s=argv[argno]+2; *s!=EOS;)
286                                 {
287                                         if (*s++ == '=')
288                                         {
289                                                 s[-1] = EOS;
290                                                 break;
291                                         }
292                                 }
293
294                                 if (argv[argno][2] == EOS)
295                                 {
296                                         printf("-d: empty symbol\n");
297                                         errcnt++;
298                                         return errcnt;
299                                 }
300
301                                 sy = lookup(argv[argno] + 2, 0, 0);
302
303                                 if (sy == NULL)
304                                 {
305                                         sy = NewSymbol(argv[argno] + 2, LABEL, 0);
306                                         sy->svalue = 0;
307                                 }
308
309                                 sy->sattr = DEFINED | EQUATED | ABS;
310                                 sy->svalue = (*s ? (VALUE)atoi(s) : 0);
311                                 break;
312                         case 'e':                               // Redirect error message output
313                         case 'E':
314                                 err_fname = argv[argno] + 2;
315                                 break;
316                         case 'f':                               // -f<format>
317                         case 'F':
318                                 switch (argv[argno][2])
319                                 {
320                                 case EOS:
321                                 case 'a':                       // -fa = Alcyon [the default]
322                                 case 'A':
323                                         obj_format = ALCYON;
324                                         break;
325                                 case 'b':                       // -fb = BSD (Jaguar Recommended: 3 out 4 jaguars recommend it!)
326                                 case 'B':
327                                         obj_format = BSD;
328                                         break;
329                                 case 'e':                       // -fe = ELF
330                                 case 'E':
331                                         obj_format = ELF;
332                                         break;
333                                 case 'x':                       // -fx = COM/EXE/XEX
334                                 case 'X':
335                                         obj_format = XEX;
336                                         break;
337                                 default:
338                                         printf("-f: unknown object format specified\n");
339                                         errcnt++;
340                                         return errcnt;
341                                 }
342                                 break;
343                         case 'g':                               // Debugging flag
344                         case 'G':
345                                 printf("Debugging flag (-g) not yet implemented\n");
346                                 break;
347                         case 'i':                               // Set directory search path
348                         case 'I':
349                                 searchpath = argv[argno] + 2;
350                                 break;
351                         case 'l':                               // Produce listing file
352                         case 'L':
353                                 if (*(argv[argno] + 2) == '*')
354                                 {
355                                         list_fname = argv[argno] + 3;
356                                         list_pag = 0;    // Special case - turn off pagination
357                                 }
358                                 else
359                                 {
360                                         list_fname = argv[argno] + 2;
361                                 }
362
363                                 listing = 1;
364                                 list_flag = 1;
365                                 lnsave++;
366                                 break;
367                         case 'o':                               // Direct object file output
368                         case 'O':
369                                 if (argv[argno][2] != EOS)
370                                         objfname = argv[argno] + 2;
371                                 else
372                                 {
373                                         if (++argno >= argc)
374                                         {
375                                                 printf("Missing argument to -o");
376                                                 errcnt++;
377                                                 return errcnt;
378                                         }
379
380                                         objfname = argv[argno];
381                                 }
382
383                                 break;
384                         case 'p':               /* -p: generate ".PRG" executable output */
385                         case 'P':
386                                 /*
387                                  * -p           .PRG generation w/o symbols
388                                  * -ps  .PRG generation with symbols
389                                  */
390                                 switch (argv[argno][2])
391                                 {
392                                         case EOS:
393                                                 prg_flag = 1;
394                                                 break;
395
396                                         case 's':
397                                         case 'S':
398                                                 prg_flag = 2;
399                                                 break;
400
401                                         default:
402                                                 printf("-p: syntax error\n");
403                                                 ++errcnt;
404                                                 return errcnt;
405                                 }
406
407                                 // Enforce Alcyon object format - kind of silly
408                                 // to ask for .prg output without it!
409                                 obj_format = ALCYON;
410                                 break;
411                         case 'r':                               // Pad seg to requested boundary size
412                         case 'R':
413                                 switch(argv[argno][2])
414                                 {
415                                 case 'w': case 'W': segpadsize = 2;  break;
416                                 case 'l': case 'L': segpadsize = 4;  break;
417                                 case 'p': case 'P': segpadsize = 8;  break;
418                                 case 'd': case 'D': segpadsize = 16; break;
419                                 case 'q': case 'Q': segpadsize = 32; break;
420                                 default: segpadsize = 2; break; // Effective autoeven();
421                                 }
422                                 break;
423                         case 's':                               // Warn about possible short branches
424                         case 'S':
425                                 sbra_flag = 1;
426                                 break;
427                         case 'u':                               // Make undefined symbols .globl
428                         case 'U':
429                                 glob_flag = 1;
430                                 break;
431                         case 'v':                               // Verbose flag
432                         case 'V':
433                                 verb_flag++;
434
435                                 if (verb_flag > 1)
436                                         DisplayVersion();
437
438                                 break;
439                         case 'x':                               // Turn on debugging
440                         case 'X':
441                                 debug = 1;
442                                 printf("~ Debugging ON\n");
443                                 break;
444                         case 'y':                               // -y<pagelen>
445                         case 'Y':
446                                 pagelen = atoi(argv[argno] + 2);
447
448                                 if (pagelen < 10)
449                                 {
450                                         printf("-y: bad page length\n");
451                                         ++errcnt;
452                                         return errcnt;
453                                 }
454
455                                 break;
456                         case EOS:                               // Input is stdin
457                                 if (firstfname == NULL) // Kludge first filename
458                                         firstfname = defname;
459
460                                 include(0, "(stdin)");
461                                 Assemble();
462                                 break;
463                         case 'h':                               // Display command line usage
464                         case 'H':
465                         case '?':
466                                 DisplayVersion();
467                                 DisplayHelp();
468                                 errcnt++;
469                                 break;
470                         case 'n':                               // Turn off legacy mode
471                         case 'N':
472                                 legacy_flag = 0;
473                                 printf("Legacy mode OFF\n");
474                                 break;
475                         default:
476                                 DisplayVersion();
477                                 printf("Unknown switch: %s\n\n", argv[argno]);
478                                 DisplayHelp();
479                                 errcnt++;
480                                 break;
481                         }
482                 }
483                 else if (*argv[argno] == '+' || *argv[argno] == '~')
484                 {
485                         if (ParseOptimization(argv[argno]) != OK)
486                         {
487                                 DisplayVersion();
488                                 printf("Unknown switch: %s\n\n", argv[argno]);
489                                 DisplayHelp();
490                                 errcnt++;
491                                 break;
492                         }
493                 }
494                 else
495                 {
496                         // Record first filename.
497                         if (firstfname == NULL)
498                                 firstfname = argv[argno];
499
500                         strcpy(fnbuf, argv[argno]);
501                         fext(fnbuf, ".s", 0);
502                         fd = open(fnbuf, 0);
503
504                         if (fd < 0)
505                         {
506                                 printf("Cannot open: %s\n", fnbuf);
507                                 errcnt++;
508                                 continue;
509                         }
510
511                         include(fd, fnbuf);
512                         Assemble();
513                 }
514         }
515
516         // Wind-up processing;
517         // o  save current section (no more code generation)
518         // o  do auto-even of all sections (or boundary alignment as requested
519         //    through '-r')
520     // o  save the last pc value for 6502 (if we used 6502 at all) and
521     //    detemine if the last section contains anything
522         // o  determine name of object file:
523         //    -  "foo.o" for linkable output;
524         //    -  "foo.prg" for GEMDOS executable (-p flag).
525         SaveSection();
526
527         for(i=TEXT; i<=BSS; i<<=1)
528         {
529                 SwitchSection(i);
530
531                 switch(segpadsize)
532                 {
533                 case 2:  d_even();    break;
534                 case 4:  d_long();    break;
535                 case 8:  d_phrase();  break;
536                 case 16: d_dphrase(); break;
537                 case 32: d_qphrase(); break;
538                 }
539
540                 SaveSection();
541         }
542
543         SwitchSection(M6502);
544
545         if (sloc != currentorg[0])
546         {
547                 currentorg[1] = sloc;
548                 currentorg += 2;
549         }
550
551         if (objfname == NULL)
552         {
553                 if (firstfname == NULL)
554                         firstfname = defname;
555
556                 strcpy(fnbuf, firstfname);
557                 fext(fnbuf, (prg_flag ? ".prg" : ".o"), 1);
558                 objfname = fnbuf;
559         }
560
561         // With one pass finished, go back and:
562         // (1)   run through all the fixups and resolve forward references;
563         // (1.5) ensure that remaining fixups can be handled by the linker
564         //       (`lo68' format, extended (postfix) format....)
565         // (2)   generate the output file image and symbol table;
566         // (3)   generate relocation information from left-over fixups.
567         ResolveAllFixups();                                             // Do all fixups
568         StopMark();                                                             // Stop mark tape-recorder
569
570         if (errcnt == 0)
571         {
572                 if ((fd = open(objfname, _OPEN_FLAGS, _PERM_MODE)) < 0)
573                         cantcreat(objfname);
574
575                 if (verb_flag)
576                 {
577                         s = (prg_flag ? "executable" : "object");
578                         printf("[Writing %s file: %s]\n", s, objfname);
579                 }
580
581                 WriteObject(fd);
582                 close(fd);
583
584                 if (errcnt != 0)
585                         unlink(objfname);
586         }
587
588         if (list_flag)
589         {
590                 if (verb_flag)
591                         printf("[Wrapping-up listing file]\n");
592
593                 listing = 1;
594                 symtable();
595                 close(list_fd);
596         }
597
598         if (err_flag)
599                 close(err_fd);
600
601         DEBUG dump_everything();
602
603         return errcnt;
604 }
605
606
607 //
608 // Determine processor endianess
609 //
610 int GetEndianess(void)
611 {
612         int i = 1;
613         char * p = (char *)&i;
614
615         if (p[0] == 1)
616                 return 0;
617
618         return 1;
619 }
620
621
622 //
623 // Application entry point
624 //
625 int main(int argc, char ** argv)
626 {
627         perm_verb_flag = 0;                             // Clobber "permanent" verbose flag
628         legacy_flag = 1;                                // Default is legacy mode on (:-P)
629
630         // Set legacy optimisation flags to on
631         // and everything else to off
632         memset(optim_flags, 0, OPT_COUNT * sizeof(int));
633         optim_flags[OPT_ABS_SHORT] =
634                 optim_flags[OPT_MOVEL_MOVEQ] =
635                 optim_flags[OPT_BSR_BCC_S] = 1;
636
637         cmdlnexec = argv[0];                    // Obtain executable name
638
639         endian = GetEndianess();                // Get processor endianess
640
641         // If commands were passed in, process them
642         if (argc > 1)
643                 return Process(argc - 1, argv + 1);
644
645         DisplayVersion();
646         DisplayHelp();
647
648         return 0;
649 }
650