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