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