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