Fix for "*" getting bad section attributes, reported by A. Seed.
[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 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 as68_flag;                                  // as68 kludge mode
29 int glob_flag;                                  // Assume undefined symbols are global
30 int lsym_flag;                                  // Include local symbols in object file
31 int sbra_flag;                                  // Warn about possible short branches
32 int legacy_flag;                                // Do stuff like insert code in RISC assembler
33 int obj_format;                                 // Object format flag
34 int debug;                                              // [1..9] Enable debugging levels
35 int err_flag;                                   // '-e' specified
36 int err_fd;                                             // File to write error messages to 
37 int rgpu, rdsp;                                 // Assembling Jaguar GPU or DSP code
38 int list_fd;                                    // File to write listing to
39 int regbank;                                    // RISC register bank
40 int segpadsize;                                 // Segment padding size
41 int endian;                                             // Host processor endianess
42 char * objfname;                                // Object filename pointer
43 char * firstfname;                              // First source filename
44 char * cmdlnexec;                               // Executable name, pointer to ARGV[0]
45 char * searchpath;                              // Search path for include files 
46 char defname[] = "noname.o";    // Default output filename
47
48
49 //
50 // Manipulate file extension.
51 //
52 // 'name' must be large enough to hold any possible filename. If 'stripp' is
53 // nonzero, any old extension is removed. If the file does not already have an
54 // extension, 'extension' is appended to the filename.
55 //
56 char * fext(char * name, char * extension, int stripp)
57 {
58         char * s;
59
60         // Find beginning of "real" name (strip off path)
61         char * beg = strrchr(name, SLASHCHAR);
62
63         if (beg == NULL)
64                 beg = name;
65
66         // Clobber any old extension, if requested
67         if (stripp)
68         {
69                 for(s=beg; *s && *s!='.'; ++s) 
70                         ;
71
72                 *s = '\0';
73         }
74
75         if (strrchr(beg, '.') == NULL)
76                 strcat(beg, extension);
77
78         return name;
79 }
80
81
82 //
83 // Return 'item'nth element of semicolon-seperated pathnames specified in the
84 // enviroment string 's'. Copy the pathname to 'buf'.  Return 0 if the 'item'
85 // nth path doesn't exist.
86 // 
87 // ['item' ranges from 0 to N-1, where N = #elements in search path]
88 //
89 int nthpath(char * env_var, int itemno, char * buf)
90 {
91         char * s = searchpath;
92
93         if (s == NULL)
94                 s = getenv(env_var);
95
96         if (s == NULL)
97                 return 0;
98
99         while (itemno--)
100                 while (*s != EOS && *s++ != ';')
101                         ;
102
103         if (*s == EOS)
104                 return 0;
105
106         while (*s != EOS && *s != ';')
107                 *buf++ = *s++;
108
109         *buf++ = EOS;
110
111         return 1;
112 }
113
114
115 //
116 // Display command line help
117 //
118 void DisplayHelp(void)
119 {
120         printf("Usage:\n"
121                 "    %s [options] srcfile\n"
122                 "\n"
123                 "Options:\n"
124                 "  -? or -h          Display usage information\n"
125                 "  -dsymbol[=value]  Define symbol\n"
126                 "  -e[errorfile]     Send error messages to file, not stdout\n"
127                 "  -f[format]        Output object file format\n"
128                 "                    b: BSD (use this for Jaguar)\n"
129                 "  -i[path]          Directory to search for include files\n"
130                 "  -l[filename]      Create an output listing file\n"
131                 "  -n                Don't do things behind your back in RISC assembler\n"
132                 "  -o file           Output file name\n"
133                 "  -r[size]          Pad segments to boundary size specified\n"
134                 "                    w: word (2 bytes, default alignment)\n"
135                 "                    l: long (4 bytes)\n"
136                 "                    p: phrase (8 bytes)\n"
137                 "                    d: double phrase (16 bytes)\n"
138                 "                    q: quad phrase (32 bytes)\n"
139                 "  -s                Warn about possible short branches\n"
140                 "  -u                Force referenced and undefined symbols global\n"
141                 "  -v                Set verbose mode\n"
142                 "  -y[pagelen]       Set page line length (default: 61)\n"
143                 "\n", cmdlnexec);
144 }
145
146
147 //
148 // Display version information
149 //
150 void DisplayVersion(void)
151 {
152         printf("\nReboot's Macro Assembler for Atari Jaguar\n"
153                 "Copyright (C) 199x Landon Dyer, 2011-2015 Reboot\n"
154                 "V%01i.%01i.%01i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM);
155 }
156
157
158 // 
159 // Process command line arguments and do an assembly
160 //
161 int Process(int argc, char ** argv)
162 {
163         int argno;                                              // Argument number
164         SYM * sy;                                               // Pointer to a symbol record
165         char * s;                                               // String pointer
166         int fd;                                                 // File descriptor
167         char fnbuf[FNSIZ];                              // Filename buffer 
168         int i;                                                  // Iterator
169
170         errcnt = 0;                                             // Initialise error count
171         listing = 0;                                    // Initialise listing level
172         list_flag = 0;                                  // Initialise listing flag
173         verb_flag = perm_verb_flag;             // Initialise verbose flag
174         as68_flag = 0;                                  // Initialise as68 kludge mode
175         glob_flag = 0;                                  // Initialise .globl flag
176         sbra_flag = 0;                                  // Initialise short branch flag
177         debug = 0;                                              // Initialise debug flag
178         searchpath = NULL;                              // Initialise search path
179         objfname = NULL;                                // Initialise object filename
180         list_fname = NULL;                              // Initialise listing filename
181         err_fname = NULL;                               // Initialise error filename
182         obj_format = BSD;                               // Initialise object format
183         firstfname = NULL;                              // Initialise first filename
184         err_fd = ERROUT;                                // Initialise error file descriptor
185         err_flag = 0;                                   // Initialise error flag
186         rgpu = 0;                                               // Initialise GPU assembly flag
187         rdsp = 0;                                               // Initialise DSP assembly flag
188         lsym_flag = 1;                                  // Include local symbols in object file
189         regbank = BANK_N;                               // No RISC register bank specified
190         orgactive = 0;                                  // Not in RISC org section
191         orgwarning = 0;                                 // No ORG warning issued
192         segpadsize = 2;                                 // Initialise segment padding size
193
194         // Initialise modules
195         InitSymbolTable();                              // Symbol table
196         InitTokenizer();                                // Tokenizer
197         InitLineProcessor();                    // Line processor
198         InitExpression();                               // Expression analyzer
199         InitSection();                                  // Section manager / code generator
200         InitMark();                                             // Mark tape-recorder
201         InitMacro();                                    // Macro processor
202         InitListing();                                  // Listing generator
203
204         // Process command line arguments and assemble source files
205         for(argno=0; argno<argc; ++argno)
206         {
207                 if (*argv[argno] == '-')
208                 {
209                         switch (argv[argno][1])
210                         {
211                         case 'd':                               // Define symbol
212                         case 'D':
213                                 for(s=argv[argno]+2; *s!=EOS;)
214                                 {
215                                         if (*s++ == '=')
216                                         {
217                                                 s[-1] = EOS;
218                                                 break;
219                                         }
220                                 }
221
222                                 if (argv[argno][2] == EOS)
223                                 {
224                                         printf("-d: empty symbol\n");
225                                         errcnt++;
226                                         return errcnt;
227                                 }
228
229                                 sy = lookup(argv[argno] + 2, 0, 0);
230
231                                 if (sy == NULL)
232                                 {
233                                         sy = NewSymbol(argv[argno] + 2, LABEL, 0);
234                                         sy->svalue = 0;
235                                 }
236
237                                 sy->sattr = DEFINED | EQUATED | ABS;
238                                 sy->svalue = (*s ? (VALUE)atoi(s) : 0);
239                                 break;
240                         case 'e':                               // Redirect error message output
241                         case 'E':
242                                 err_fname = argv[argno] + 2;
243                                 break;
244                         case 'f':                               // -f<format>
245                         case 'F':
246                                 switch (argv[argno][2])
247                                 {
248                                 case EOS:
249                                 case 'b':                       // -fb = BSD (Jaguar Recommended)
250                                 case 'B':
251                                         obj_format = BSD;
252                                         break;
253                                 default:
254                                         printf("-f: unknown object format specified\n");
255                                         errcnt++;
256                                         return errcnt;
257                                 }
258                                 break;
259                         case 'g':                               // Debugging flag
260                         case 'G':
261                                 printf("Debugging flag (-g) not yet implemented\n");
262                                 break;
263                         case 'i':                               // Set directory search path
264                         case 'I':
265                                 searchpath = argv[argno] + 2;
266                                 break;
267                         case 'l':                               // Produce listing file
268                         case 'L':
269                                 list_fname = argv[argno] + 2;
270                                 listing = 1;
271                                 list_flag = 1;
272                                 lnsave++;
273                                 break;
274                         case 'o':                               // Direct object file output
275                         case 'O':
276                                 if (argv[argno][2] != EOS)
277                                         objfname = argv[argno] + 2;
278                                 else
279                                 {
280                                         if (++argno >= argc)
281                                         {
282                                                 printf("Missing argument to -o");
283                                                 errcnt++;
284                                                 return errcnt;
285                                         }
286                                         objfname = argv[argno];
287                                 }
288
289                                 break;
290                         case 'r':                               // Pad seg to requested boundary size
291                         case 'R':
292                                 switch(argv[argno][2])
293                                 {
294                                 case 'w': case 'W': segpadsize = 2;  break;  
295                                 case 'l': case 'L': segpadsize = 4;  break;
296                                 case 'p': case 'P': segpadsize = 8;  break;
297                                 case 'd': case 'D': segpadsize = 16; break;
298                                 case 'q': case 'Q': segpadsize = 32; break;
299                                 default: segpadsize = 2; break; // Effective autoeven();
300                                 }
301                                 break;
302                         case 's':                               // Warn about possible short branches
303                         case 'S':
304                                 sbra_flag = 1;
305                                 break;
306                         case 'u':                               // Make undefined symbols .globl
307                         case 'U':
308                                 glob_flag = 1;
309                                 break;
310                         case 'v':                               // Verbose flag
311                         case 'V':
312                                 verb_flag++;
313
314                                 if (verb_flag > 1)
315                                         DisplayVersion();
316
317                                 break;
318                         case 'x':                               // Turn on debugging
319                         case 'X':
320                                 debug = 1;
321                                 printf("~ Debugging ON\n");
322                                 break;
323                         case 'y':                               // -y<pagelen>
324                         case 'Y':
325                                 pagelen = atoi(argv[argno] + 2);
326
327                                 if (pagelen < 10)
328                                 {
329                                         printf("-y: bad page length\n");
330                                         ++errcnt;
331                                         return errcnt;
332                                 }
333
334                                 break;
335                         case EOS:                               // Input is stdin
336                                 if (firstfname == NULL) // Kludge first filename
337                                         firstfname = defname;
338
339                                 include(0, "(stdin)");
340                                 Assemble();
341                                 break;
342                         case 'h':                               // Display command line usage
343                         case 'H':
344                         case '?':
345                                 DisplayVersion();
346                                 DisplayHelp();
347                                 errcnt++;
348                                 break;
349                         case 'n':                               // Turn off legacy mode
350                         case 'N':
351                                 legacy_flag = 0;
352                                 printf("Legacy mode OFF\n");
353                                 break;
354                         default:
355                                 DisplayVersion();
356                                 printf("Unknown switch: %s\n\n", argv[argno]);
357                                 DisplayHelp();
358                                 errcnt++;
359                                 break;
360                         }
361                 }
362                 else
363                 {
364                         // Record first filename.
365                         if (firstfname == NULL)
366                                 firstfname = argv[argno];
367
368                         strcpy(fnbuf, argv[argno]);
369                         fext(fnbuf, ".s", 0);
370                         fd = open(fnbuf, 0);
371
372                         if (fd < 0)
373                         {
374                                 printf("Cannot open: %s\n", fnbuf);
375                                 errcnt++;
376                                 continue;
377                         }
378
379                         include(fd, fnbuf);
380                         Assemble();
381                 }
382         }
383
384         // Wind-up processing;
385         // o  save current section (no more code generation)
386         // o  do auto-even of all sections (or boundary alignment as requested
387         //    through '-r')
388         // o  determine name of object file:
389         //    -  "foo.o" for linkable output;
390         //    -  "foo.prg" for GEMDOS executable (-p flag).
391         SaveSection();
392
393         for(i=TEXT; i<=BSS; i<<=1)
394         {
395                 SwitchSection(i);
396
397                 switch(segpadsize)
398                 {
399                 case 2:  d_even();    break;
400                 case 4:  d_long();    break;
401                 case 8:  d_phrase();  break;
402                 case 16: d_dphrase(); break;
403                 case 32: d_qphrase(); break;
404                 }
405
406                 SaveSection();
407         }
408
409         if (objfname == NULL)
410         {
411                 if (firstfname == NULL)
412                         firstfname = defname;
413
414                 strcpy(fnbuf, firstfname);
415                 //fext(fnbuf, prg_flag ? ".prg" : ".o", 1);
416                 fext(fnbuf, ".o", 1);
417                 objfname = fnbuf;
418         }
419
420         // With one pass finished, go back and:
421         // (1)   run through all the fixups and resolve forward references;
422         // (1.5) ensure that remaining fixups can be handled by the linker
423         //       (`lo68' format, extended (postfix) format....)
424         // (2)   generate the output file image and symbol table;
425         // (3)   generate relocation information from left-over fixups.
426         ResolveAllFixups();                                             // Do all fixups
427         StopMark();                                                             // Stop mark tape-recorder
428
429         if (errcnt == 0)
430         {
431                 if ((fd = open(objfname, _OPEN_FLAGS, _PERM_MODE)) < 0)
432                         cantcreat(objfname);
433
434                 if (verb_flag)
435                 {
436                         s = "object";
437                         printf("[Writing %s file: %s]\n", s, objfname);
438                 }
439
440                 WriteObject(fd);
441                 close(fd);
442
443                 if (errcnt != 0)
444                         unlink(objfname);
445         }
446
447         if (list_flag)
448         {
449                 if (verb_flag)
450                         printf("[Wrapping-up listing file]\n");
451
452                 listing = 1;
453                 symtable();
454                 close(list_fd);
455         }
456
457         if (err_flag)
458                 close(err_fd);
459
460         DEBUG dump_everything();
461
462         return errcnt;
463 }
464
465
466 //
467 // Determine processor endianess
468 //
469 int GetEndianess(void)
470 {
471         int i = 1;
472         char * p = (char *)&i;
473
474         if (p[0] == 1)
475                 return 0;
476
477         return 1;
478 }
479
480
481 //
482 // Application entry point
483 //
484 int main(int argc, char ** argv)
485 {
486         perm_verb_flag = 0;                             // Clobber "permanent" verbose flag
487         legacy_flag = 1;                                // Default is legacy mode on (:-P)
488         cmdlnexec = argv[0];                    // Obtain executable name
489
490         endian = GetEndianess();                // Get processor endianess
491
492         // If commands were passed in, process them
493         if (argc > 1)
494                 return Process(argc - 1, argv + 1);              
495
496         DisplayVersion();
497         DisplayHelp();
498
499         return 0;
500 }
501