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