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