*** empty log message ***
[fw/sdcc] / as / xa51 / xa_main.c
1 /* Paul's XA51 Assembler, Copyright 1997,2002 Paul Stoffregen (paul@pjrc.com)
2  *
3  * Paul's XA51 Assembler is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; version 2.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /* adapted from the osu8asm project, 1995 */
18 /* http://www.pjrc.com/tech/osu8/index.html */
19
20 /* 
21    made "relocatable" by johan.knol@iduna.nl for sdcc
22    
23    This isn't a standalone assembler anymore. It's only purpose is to
24    create relocatable modules (that has to be processed with xa_link) 
25    out of sdcc-generated .xa files
26 */
27
28 #define D(x) x
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <limits.h>
34
35 #define printf(x...) fprintf(stderr,x)
36
37 #include "xa_main.h"
38 #include "xa_version.h"
39 extern void yyrestart(FILE *new_file);
40 extern int yyparse();
41
42
43 char modulename[PATH_MAX];
44 char infilename[PATH_MAX];
45 char outfilename[PATH_MAX];
46 char listfilename[PATH_MAX];
47 char symfilename[PATH_MAX];
48
49 /* global variables */
50
51 FILE *frel, *fmem, *list_fp, *sym_fp;
52 extern FILE *yyin;
53 extern char *yytext;
54 extern char last_line_text[];
55 struct symbol *sym_list=NULL;
56 struct target *targ_list=NULL;
57 int lineno=1;
58 int p1=0, p2=0, p3=0;
59 int expr_result, expr_ok, jump_dest, inst;
60 int opcode, operand;
61 char symbol_name[1000];
62 struct area_struct area[NUM_AREAS];
63 int current_area=0;
64
65 char rel_line[2][132];
66
67 char *areaToString (int area) {
68   switch (area) 
69     {
70     case AREA_CSEG: return "CSEG";
71     case AREA_DSEG: return "DSEG";
72       //case AREA_OSEG: return "OSEG";
73       //case AREA_ISEG: return "ISEG";
74     case AREA_BSEG: return "BSEG";
75     case AREA_XSEG: return "XSEG";
76     case AREA_XISEG: return "XISEG";
77     case AREA_XINIT: return "XINIT";
78     case AREA_GSINIT: return "GSINIT";
79     case AREA_GSFINAL: return "GSFINAL";
80     case AREA_HOME: return "HOME";
81     case AREA_SSEG: return "SSEG";
82     }
83   return ("UNKNOW");
84 }
85
86 /* "mem" is replaced by area[current_area].alloc_position */
87 /* int mem=0; */   /* mem is location in memory */
88
89 /* add symbols to list when we find their definition in pass #1 */
90 /* we will evaluate their values in pass #2, and figure out if */
91 /* they are branch targets betweem passes 1 and 2.  Every symbol */
92 /* should appear exactly once in this list, since it can't be redefined */
93
94 struct symbol * build_sym_list(char *thename)
95 {
96         struct symbol *new, *p;
97
98         if ((p=findSymbol(thename))) {
99           p->area=current_area;
100           //fprintf (stderr, "warning, symbol %s already defined\n", thename);
101           return p;
102         }
103
104         //printf("  Symbol: %s  Line: %d\n", thename, lineno);
105         new = (struct symbol *) malloc(sizeof(struct symbol));
106         new->name = (char *) malloc(strlen(thename)+1);
107         strcpy(new->name, thename);
108         new->value = 0;
109         new->istarget = 0;
110         new->isdef = 0;
111         new->isbit = 0;
112         new->isreg = 0;
113         new->line_def = lineno - 1;
114         new->area = current_area;
115         new->mode = 'X'; // start with an external
116         new->next = NULL;
117         if (sym_list == NULL) return (sym_list = new);
118         p = sym_list;
119         while (p->next != NULL) p = p->next;
120         p->next = new;
121         return (new);
122 }
123
124 struct symbol *findSymbol (char *thename) {
125   struct symbol *p;
126   for (p=sym_list; p; p=p->next) {
127     if (strcasecmp(thename, p->name)==0) {
128       return p;
129     }
130   }
131   return NULL;
132 }
133
134 int assign_value(char *thename, int thevalue, char mode)
135 {
136         struct symbol *p;
137
138         p = sym_list;
139         while (p != NULL) {
140                 if (!(strcasecmp(thename, p->name))) {
141                         p->value = thevalue;
142                         p->isdef = 1;
143                         p->mode = mode;
144                         return (0);
145                 }
146                 p = p->next;
147         }
148         fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
149         exit(1);
150 }
151
152 int mk_bit(char *thename, int area)
153 {
154         struct symbol *p;
155
156         p = sym_list;
157         while (p != NULL) {
158                 if (!(strcasecmp(thename, p->name))) {
159                         p->isbit = 1;
160                         p->area = area;
161                         return (0);
162                 }
163                 p = p->next;
164         }
165         fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
166         exit(1);
167 }
168
169 int mk_sfr(char *thename)
170 {
171         struct symbol *p;
172
173         p = sym_list;
174         while (p != NULL) {
175                 if (!(strcasecmp(thename, p->name))) {
176                         p->issfr = 1;
177                         p->area = 0;
178                         return (0);
179                 }
180                 p = p->next;
181         }
182         fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
183         exit(1);
184 }
185
186
187 int mk_reg(char *thename)
188 {
189         struct symbol *p;
190
191         p = sym_list;
192         while (p != NULL) {
193                 if (!(strcasecmp(thename, p->name))) {
194                         p->isreg = 1;
195                         return (0);
196                 }
197                 p = p->next;
198         }
199         fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
200         exit(1);
201 }
202
203 int mk_global(char *thename)
204 {
205   struct symbol *p;
206   
207   p = sym_list;
208   while (p != NULL) {
209     if (!(strcasecmp(thename, p->name))) {
210       p->global = 1;
211       return (0);
212     }
213     p = p->next;
214   }
215   fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
216   exit(1);
217 }
218
219 int get_value(char *thename)
220 {
221   struct symbol *p;
222   p = sym_list;
223   while (p != NULL) {
224     if (!(strcasecmp(thename, p->name))) {
225       if (p->mode=='=')
226         return 0;
227       return (p->value);
228     }
229     p = p->next;
230   }
231   fprintf(stderr, "Internal Error!  Couldn't find symbol value\n");
232   exit(1);
233 }
234                 
235
236
237 /* add every branch target to this list as we find them */
238 /* ok if multiple entries of same symbol name in this list */
239
240 struct target * build_target_list(char *thename)
241 {
242         struct target *new, *p;
243         new = (struct target *) malloc(sizeof(struct target));
244         new->name = (char *) malloc(strlen(thename)+1);
245         strcpy(new->name, thename);
246         new->next = NULL;
247         if (targ_list == NULL) return (targ_list = new);
248         p = targ_list;
249         while (p->next != NULL) p = p->next;
250         p->next = new;
251         return (new);
252 }
253
254 /* figure out which symbols are branch targets */
255
256 void flag_targets()
257 {
258         struct symbol *p_sym;
259         struct target *p_targ;
260         p_targ = targ_list;
261         while (p_targ != NULL) {
262                 p_sym = sym_list;
263                 while (p_sym != NULL) {
264                         if (!strcasecmp(p_sym->name, p_targ->name))
265                                 p_sym->istarget = 1;
266                         p_sym = p_sym->next;
267                 }
268                 p_targ = p_targ->next;
269         }
270 }
271
272 void print_symbol_table()
273 {
274   struct symbol *p;
275   p = sym_list;
276   while (p != NULL) {
277 #if 0
278     fprintf(sym_fp, "Sym in %-5s: %s\n", areaToString(p->area), p->name);
279     fprintf(sym_fp, "  at: 0x%04X (%5d)", p->value, p->value);
280     fprintf(sym_fp, " Def:%s", p->isdef ? "Yes" : "No ");
281     fprintf(sym_fp, " Bit:%s", p->isbit ? "Yes" : "No ");
282     fprintf(sym_fp, " Target:%s", p->istarget ? "Yes" : "No ");
283     fprintf(sym_fp, " Line %d\n", p->line_def);
284 #else
285     if (p->issfr) {
286       fprintf (sym_fp, "%-5s", "SFR");
287     } else if (p->isbit && !p->area) {
288       fprintf (sym_fp, "%-5s", "SBIT");
289     } else if (p->mode=='=') {
290       fprintf (sym_fp,"ABS");
291     } else if (!p->isdef) {
292       fprintf (sym_fp,"EXTRN");
293     } else {
294       fprintf (sym_fp, "%-5s", areaToString(p->area));
295     }
296     fprintf (sym_fp, " 0x%04x (%5d)", p->value, p->value);
297     fprintf (sym_fp, " %s", p->isdef ? "D" : "-");
298     fprintf (sym_fp, "%s", p->isbit ? "B" : "-");
299     fprintf (sym_fp, "%s", p->istarget ? "T" : "-");
300     fprintf (sym_fp, " %s\n", p->name);
301 #endif
302     p = p->next;
303   }
304 }
305
306 /* check that every symbol is in the table only once */
307
308 void check_redefine()
309 {
310   struct symbol *p1, *p2;
311   p1 = sym_list;
312   while (p1 != NULL) {
313     p2 = p1->next;
314     while (p2 != NULL) {
315       if (!strcasecmp(p1->name, p2->name)) {
316         fprintf(stderr, "Error: symbol '%s' redefined on line %d", 
317                 p1->name, p2->line_def);
318         fprintf(stderr, ", first defined on line %d\n", p1->line_def);
319         exit(1);
320       }
321       p2 = p2->next;
322     }
323     p1 = p1->next;
324   }
325 }
326
327 int is_target(char *thename)
328 {
329         struct symbol *p;
330         p = sym_list;
331         while (p != NULL) {
332                 if (!strcasecmp(thename, p->name)) return (p->istarget);
333                 p = p->next;
334         }
335         return (0);
336 }
337
338 int is_bit(char *thename)
339 {
340         struct symbol *p;
341         p = sym_list;
342         while (p != NULL) {
343                 if (!strcasecmp(thename, p->name)) return (p->isbit);
344                 p = p->next;
345         }
346         return (0);
347 }
348
349 int is_reg(char *thename)
350 {
351         struct symbol *p;
352         p = sym_list;
353         while (p != NULL) {
354                 if (!strcasecmp(thename, p->name)) return (p->isreg);
355                 p = p->next;
356         }
357         return (0);
358 }
359
360
361 struct symbol *is_def(char *thename)
362 {
363   struct symbol *p;
364   p = sym_list;
365   while (p != NULL) {
366     if (!strcasecmp(thename, p->name) && p->isdef) 
367       return p;
368     p = p->next;
369   }
370   return NULL;
371 }
372
373 struct symbol *is_ref(char *thename) {
374   struct symbol *p;
375   p = sym_list;
376   while (p != NULL) {
377     if (strcasecmp(thename, p->name)==0) 
378       return p;
379     p = p->next;
380   }
381   return NULL;
382 }
383
384 /* this routine is used to dump a group of bytes to the output */
385 /* it is responsible for generating the list file and sending */
386 /* the bytes one at a time to the object code generator */
387 /* this routine is also responsible for generatine the list file */
388 /* though is it expected that the lexer has placed all the actual */
389 /* original text from the line in "last_line_text" */
390
391 static short last_area=-1;
392
393 int debug=0;
394
395 void out(int *byte_list, int num) {
396   struct symbol *p;
397   int i, first=1;
398   
399   if (num > 0) fprintf(list_fp, "%06X: ", MEM_POS);
400   else fprintf(list_fp, "\t");
401   
402   if (last_area!=current_area) {
403     // emit area information
404     if (area[current_area].size) {
405       fprintf (frel, "A %s size %d flags 0\n", 
406                areaToString(current_area),
407                area[current_area].size);
408       if  (!area[current_area].defsEmitted) {
409         for (p=sym_list; p; p=p->next) {
410           if (p->global && p->isdef && p->area==current_area) {
411             // skip temp labels
412             if (p->name[strlen(p->name)-1]!='$') {
413               if (p->mode=='=') {
414                 fprintf (frel, "S %s Abs%04x\n", p->name, p->value);
415               } else {
416                 fprintf (frel, "S %s Def%04x\n", p->name, p->value);
417               }
418             }
419           }
420         }
421         area[current_area].defsEmitted=1;
422       }
423     }
424     last_area=current_area;
425   }
426   if (current_area==AREA_CSEG ||
427       current_area==AREA_GSINIT ||
428       current_area==AREA_GSFINAL ||
429       current_area==AREA_XINIT) {
430     if (num) {
431       for (i=0; i<num; i++) {
432         if ((i%16)==0) {
433           fprintf (frel, "%sT %04x", i ? "\n" : "", MEM_POS+i);
434         }
435         fprintf (frel, " %02x", byte_list[i]);
436       }
437       fprintf (frel, "\n");
438       if (rel_line[0][0]) {
439         fprintf (frel, "%s\n", rel_line[0]);
440         if (rel_line[1][0]) {
441           fprintf (frel, "%s\n", rel_line[1]);
442         }
443       }
444     }
445     for (i=0; i<num; i++) {
446       if (!first && (i % 4) == 0) fprintf(list_fp, "\t");
447       fprintf(list_fp, "%02X", byte_list[i]);
448       if ((i+1) % 4 == 0) {
449         if (first) fprintf(list_fp, "\t%s\n", last_line_text);
450         else fprintf(list_fp, "\n");
451         first = 0;
452       } else {
453         if (i<num-1) fprintf(list_fp, " ");
454       }
455     }
456   }
457   if (first) {
458     if (num < 3) fprintf(list_fp, "\t");
459     fprintf(list_fp, "\t%s\n", last_line_text);
460   } else {
461     if (num % 4) fprintf(list_fp, "\n");
462   }
463   expr_var[0][0]='\0';
464   expr_var[1][0]='\0';
465   rel_line[0][0]='\0';
466   rel_line[1][0]='\0';
467 }
468
469
470 /* add NOPs to align memory location on a valid branch target address */
471
472 void pad_with_nop()
473 {
474         static int nops[] = {NOP_OPCODE, NOP_OPCODE, NOP_OPCODE, NOP_OPCODE};
475         int num;
476
477         last_line_text[0] = '\0';
478
479         for(num=0; (MEM_POS + num) % BRANCH_SPACING; num++) ;
480         if (p3) out(nops, num);
481         MEM_POS += num;
482 }
483
484 /* print branch out of bounds error */
485
486 void boob_error()
487 {
488         fprintf(stderr, "Error: branch out of bounds");
489         fprintf(stderr, " in line %d\n", lineno);
490         exit(1);
491 }
492
493 /* output the jump either direction on carry */
494 /* jump_dest and MEM_POS must have the proper values */
495
496 /* 
497 void do_jump_on_carry()
498 {
499         if (p3) {
500                 operand = REL4(jump_dest, MEM_POS);
501                 if (operand < 0) {
502                         operand *= -1;
503                         operand -= 1;
504                         if (operand > 15) boob_error();
505                         out(0x20 + (operand & 15));
506                 } else {
507                         if (operand > 15) boob_error();
508                         out(0x30 + (operand & 15));
509                 }
510         }
511 }
512 */ 
513
514 /* turn a string like "10010110b" into an int */
515
516 int binary2int(char *str)
517 {
518         register int i, j=1, sum=0;
519         
520         for (i=strlen(str)-2; i >= 0; i--) {
521                 sum += j * (str[i] == '1');
522                 j *= 2;
523         }
524         return (sum);
525 }
526
527 void print_usage(int);
528
529 void init_areas(void)
530 {
531   area[AREA_CSEG].start=area[AREA_CSEG].alloc_position = 0;
532   area[AREA_DSEG].start=area[AREA_DSEG].alloc_position = 0;
533   area[AREA_BSEG].start=area[AREA_BSEG].alloc_position = 0;
534   area[AREA_XSEG].start=area[AREA_XSEG].alloc_position = 0;
535   area[AREA_XISEG].start=area[AREA_XISEG].alloc_position = 0;
536   area[AREA_XINIT].start=area[AREA_XINIT].alloc_position = 0;
537   area[AREA_GSINIT].start=area[AREA_GSINIT].alloc_position = 0;
538   area[AREA_GSFINAL].start=area[AREA_GSFINAL].alloc_position = 0;
539   area[AREA_HOME].start=area[AREA_HOME].alloc_position = 0;
540 }
541
542 void relPrelude() {
543   //char buffer[132];
544   int i, areas=0, globals=0;
545   struct symbol *p;
546
547   fprintf (frel, "SDCCXA rel, version %1.1f\n", version);
548   for (i=1; i<NUM_AREAS; i++) {
549     if ((area[i].size=area[i].alloc_position-area[i].start)) {
550       areas++;
551     }
552   }
553   for (p=sym_list; p; p=p->next) {
554     if (p->isdef) {
555       // skip temp labels, sfr and sbit
556       if (p->name[strlen(p->name)-1]!='$' &&
557           p->area) {
558         globals++;
559       }
560     }
561   }
562   fprintf (frel, "H %d areas %d global symbols\n", areas, globals);
563   fprintf (frel, "M %s\n", modulename);
564   for (p=sym_list; p; p=p->next) {
565     if (!p->isdef) {
566       fprintf (frel, "S %s Ref0000\n", p->name);
567     }
568   }
569 }
570
571 void printVersion() {
572   printf("\nPaul's XA51 Assembler\n");
573   printf("Copyright 1997,2002 Paul Stoffregen\n\n");
574   printf("This program is free software; you can redistribute it\n");
575   printf("and/or modify it under the terms of the GNU General Public\n");
576   printf("License, Version 2, published by the Free Software Foundation\n\n");
577   printf("This program is distributed in the hope that it will be useful,\n");
578   printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
579   printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
580 }
581
582 int verbose=0, createSymbolFile=0;
583
584 void process_args(int argc, char **argv) 
585 {
586   int i=0;
587
588   if (argc < 2) print_usage(1);
589   
590   while (++i<argc && *argv[i]=='-') {
591     if (strcmp(argv[i], "--version")==0) {
592       printVersion();
593       exit (0);
594     }
595     if (strcmp(argv[i], "--help")==0) {
596       print_usage(0);
597     }
598     if (strcmp(argv[i], "-v")==0) {
599       verbose++;
600       continue;
601     }
602     if (strcmp(argv[i], "-s")==0) {
603       createSymbolFile++;
604       continue;
605     }
606     print_usage(1);
607   }
608
609   if (i!=argc-1) {
610     // only 1 source file for now
611     print_usage(1);
612   }
613
614   strcpy(infilename, argv[i]);
615
616   if (strncasecmp(infilename+strlen(infilename)-4, ".asm", 3)) {
617     fprintf (stderr, "unrecognized input file: \"%s\"\n", argv[i]);
618     print_usage(1);
619   }
620
621   strcpy(modulename, infilename);
622   modulename[strlen(modulename)-4] = '\0';
623   sprintf (outfilename, "%s.rel", modulename);
624   sprintf (listfilename, "%s.lst", modulename);
625   if (createSymbolFile) {
626     sprintf (symfilename, "%s.sym", modulename);
627   }
628 }
629
630 /* pass #1 (p1=1) find all symbol defs and branch target names */
631 /* pass #2 (p2=1) align branch targets, evaluate all symbols */
632 /* pass #3 (p3=1) produce object code */
633
634 int main(int argc, char **argv)
635 {
636         process_args (argc, argv);
637
638         yyin = fopen(infilename, "r");
639         if (yyin == NULL) {
640                 fprintf(stderr, "Can't open file '%s'.\n", infilename);
641                 exit(1);
642         }
643         frel = fopen(outfilename, "w");
644         if (frel == NULL) {
645                 fprintf(stderr, "Can't write file '%s'.\n", outfilename);
646                 exit(1);
647         }
648         list_fp = fopen(listfilename, "w");
649         if (list_fp == NULL) {
650                 fprintf(stderr, "Can't write file '%s'.\n", listfilename);
651                 exit(1);
652         }
653         if (createSymbolFile) {
654           sym_fp = fopen(symfilename, "w");
655           if (sym_fp == NULL) {
656             fprintf(stderr, "Can't write file '%s'.\n", symfilename);
657             exit(1);
658           }
659         }
660
661         if (verbose) printf("Pass 1: Building Symbol Table:\n");
662         p1 = 1;
663         init_areas();
664         yyparse();
665         flag_targets();
666         check_redefine();
667
668         if (verbose) printf("Pass 2: Aligning Branch Targets:\n");
669         p1 = 0;
670         p2 = 1;
671         rewind(yyin);
672         yyrestart(yyin);
673         lineno = 1;
674         init_areas();
675         yyparse();
676
677         relPrelude();
678         if (createSymbolFile) print_symbol_table();
679
680         if (verbose) printf("Pass 3: Generating Object Code:\n");
681         p2 = 0;
682         p3 = 1;
683         rewind(yyin);
684         yyrestart(yyin);
685         lineno = 1;
686         init_areas();
687         yyparse();
688
689         fclose(yyin);
690         return 0;
691 }
692
693
694 void print_usage(int fatal)
695 {
696   FILE *out = fatal ? stderr : stdout;
697
698   fprintf (out, "Usage: xa_asm [-s] [-v] file.xa\n");
699   fprintf (out, "  -v            verbose: show progress\n");
700   fprintf (out, "  -s            create symbol file\n");
701   fprintf (out, "  --version     show version/copyright info and exit\n");
702   fprintf (out, "  --help        show this and exit\n");
703 #if 0
704   // some usefull options I can think of.
705   fprintf (out, "  -m            create map file\n");
706   fprintf (out, "  -ss           create symbol file sorted by symbol\n");
707   fprintf (out, "  -sa           create symbol file sorted by segment/address\n");
708   fprintf (out, "  --no-temps    supress temp symbols in map and sym file\n");
709   fprintf (out, "  --code-loc=#  sets the start address of the code\n");
710   fprintf (out, "  --xdata-loc=# sets the start address of the external data\n");
711   fprintf (out, "  --stack-loc=# sets the start address of the stack\n");
712 #endif
713   exit(fatal);
714 }
715