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