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) {
236       fprintf (sym_fp, "%-5s", "BIT");
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         for (i=0; i<num; i++) {
330                 hexout(byte_list[i], MEM_POS + i, 0);
331                 if (!first && (i % 4) == 0) fprintf(list_fp, "\t");
332                 fprintf(list_fp, "%02X", byte_list[i]);
333                 if ((i+1) % 4 == 0) {
334                         if (first) fprintf(list_fp, "\t%s\n", last_line_text);
335                         else fprintf(list_fp, "\n");
336                         first = 0;
337                 } else {
338                         if (i<num-1) fprintf(list_fp, " ");
339                 }
340         }
341         if (first) {
342                 if (num < 3) fprintf(list_fp, "\t");
343                 fprintf(list_fp, "\t%s\n", last_line_text);
344         } else {
345                 if (num % 4) fprintf(list_fp, "\n");
346         }
347 }
348
349
350 /* add NOPs to align memory location on a valid branch target address */
351
352 void pad_with_nop()
353 {
354         static int nops[] = {NOP_OPCODE, NOP_OPCODE, NOP_OPCODE, NOP_OPCODE};
355         int num;
356
357         last_line_text[0] = '\0';
358
359         for(num=0; (MEM_POS + num) % BRANCH_SPACING; num++) ;
360         if (p3) out(nops, num);
361         MEM_POS += num;
362 }
363
364 /* print branch out of bounds error */
365
366 void boob_error()
367 {
368         fprintf(stderr, "Error: branch out of bounds");
369         fprintf(stderr, " in line %d\n", lineno);
370         exit(1);
371 }
372
373 /* output the jump either direction on carry */
374 /* jump_dest and MEM_POS must have the proper values */
375
376 /* 
377 void do_jump_on_carry()
378 {
379         if (p3) {
380                 operand = REL4(jump_dest, MEM_POS);
381                 if (operand < 0) {
382                         operand *= -1;
383                         operand -= 1;
384                         if (operand > 15) boob_error();
385                         out(0x20 + (operand & 15));
386                 } else {
387                         if (operand > 15) boob_error();
388                         out(0x30 + (operand & 15));
389                 }
390         }
391 }
392 */ 
393
394 /* turn a string like "10010110b" into an int */
395
396 int binary2int(char *str)
397 {
398         register int i, j=1, sum=0;
399         
400         for (i=strlen(str)-2; i >= 0; i--) {
401                 sum += j * (str[i] == '1');
402                 j *= 2;
403         }
404         return (sum);
405 }
406
407 void print_usage(int);
408
409
410 /* todo: someday this will allow the user to control where the */
411 /* various memory areas go, and it will take care of assigning */
412 /* positions to area which follow others (such as OSEG getting */
413 /* set just after DSEG on the 2nd and 3rd passes when we have */
414 /* leared the size needed for each segment */
415
416 void init_areas(void)
417 {
418   area[AREA_CSEG].start=area[AREA_CSEG].alloc_position = 0;
419   area[AREA_DSEG].start=area[AREA_DSEG].alloc_position = 0x30;
420   area[AREA_BSEG].start=area[AREA_BSEG].alloc_position = 0;
421   area[AREA_XSEG].start=area[AREA_XSEG].alloc_position = 0;
422   area[AREA_XISEG].start=area[AREA_XISEG].alloc_position = 0;
423   area[AREA_XINIT].start=area[AREA_XINIT].alloc_position = 0;
424   area[AREA_GSINIT].start=area[AREA_GSINIT].alloc_position = 0;
425   area[AREA_GSFINAL].start=area[AREA_GSFINAL].alloc_position = 0;
426   area[AREA_HOME].start=area[AREA_HOME].alloc_position = 0;
427 }
428
429 void addAreaSymbols() {
430   char buffer[132];
431   int i;
432   for (i=0; i<NUM_AREAS; i++) {
433     current_area=i;
434     sprintf (buffer, "s_%s", areaToString(i));
435     build_sym_list (buffer);
436     assign_value (buffer, area[i].start);
437     buffer[0]='l';
438     build_sym_list (buffer);
439     assign_value (buffer, area[i].alloc_position-area[i].start);
440   }
441 }
442
443 void printVersion() {
444   printf("\nPaul's XA51 Assembler\n");
445   printf("Copyright 1997,2002 Paul Stoffregen\n\n");
446   printf("This program is free software; you can redistribute it\n");
447   printf("and/or modify it under the terms of the GNU General Public\n");
448   printf("License, Version 2, published by the Free Software Foundation\n\n");
449   printf("This program is distributed in the hope that it will be useful,\n");
450   printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
451   printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
452 }
453
454 char infilename[PATH_MAX];
455 char outfilename[PATH_MAX];
456 char listfilename[PATH_MAX];
457 char symfilename[PATH_MAX];
458 //char mapfilename[PATH_MAX];
459
460 int verbose=0, createSymbolFile=0;
461
462 void process_args(int argc, char **argv) 
463 {
464   int i=0;
465
466   if (argc < 2) print_usage(1);
467   
468   while (++i<argc && *argv[i]=='-') {
469     if (strcmp(argv[i], "--version")==0) {
470       printVersion();
471       exit (0);
472     }
473     if (strcmp(argv[i], "--help")==0) {
474       print_usage(0);
475     }
476     if (strcmp(argv[i], "-v")==0) {
477       verbose++;
478       continue;
479     }
480     if (strcmp(argv[i], "-s")==0) {
481       createSymbolFile++;
482       continue;
483     }
484     print_usage(1);
485   }
486
487   if (i!=argc-1) {
488     // only 1 source file for now
489     print_usage(1);
490   }
491
492   strcpy(infilename, argv[i]);
493
494   if (strncasecmp(infilename+strlen(infilename)-3, ".xa", 3)) {
495     fprintf (stderr, "unrecognized input file: \"%s\"\n", argv[i]);
496     print_usage(1);
497   }
498
499   strcpy(outfilename, infilename);
500   outfilename[strlen(outfilename)-3] = '\0';
501   strcpy(listfilename, outfilename);
502   if (createSymbolFile) {
503     strcpy(symfilename, outfilename);
504     strcat(symfilename, ".sym");
505   }
506   //strcpy(mapfilename, outfilename);
507   strcat(outfilename, ".hex");
508   strcat(listfilename, ".lst");
509   //strcat(mapfilename, ".map");
510 }
511
512 /* pass #1 (p1=1) find all symbol defs and branch target names */
513 /* pass #2 (p2=1) align branch targets, evaluate all symbols */
514 /* pass #3 (p3=1) produce object code */
515
516 int main(int argc, char **argv)
517 {
518         process_args (argc, argv);
519
520         yyin = fopen(infilename, "r");
521         if (yyin == NULL) {
522                 fprintf(stderr, "Can't open file '%s'.\n", infilename);
523                 exit(1);
524         }
525         fhex = fopen(outfilename, "w");
526         if (fhex == NULL) {
527                 fprintf(stderr, "Can't write file '%s'.\n", outfilename);
528                 exit(1);
529         }
530         list_fp = fopen(listfilename, "w");
531         if (list_fp == NULL) {
532                 fprintf(stderr, "Can't write file '%s'.\n", listfilename);
533                 exit(1);
534         }
535         if (createSymbolFile) {
536           sym_fp = fopen(symfilename, "w");
537           if (sym_fp == NULL) {
538             fprintf(stderr, "Can't write file '%s'.\n", symfilename);
539             exit(1);
540           }
541         }
542
543         if (verbose) printf("Pass 1: Building Symbol Table:\n");
544         p1 = 1;
545         init_areas();
546         yyparse();
547         flag_targets();
548         check_redefine();
549
550         if (verbose) printf("Pass 2: Aligning Branch Targets:\n");
551         p1 = 0;
552         p2 = 1;
553         rewind(yyin);
554         yyrestart(yyin);
555         lineno = 1;
556         init_areas();
557         yyparse();
558
559         addAreaSymbols();
560         if (createSymbolFile) print_symbol_table();
561
562         if (verbose) printf("Pass 3: Generating Object Code:\n");
563         p2 = 0;
564         p3 = 1;
565         rewind(yyin);
566         yyrestart(yyin);
567         lineno = 1;
568         init_areas();
569         yyparse();
570
571         fclose(yyin);
572         hexout(0, 0, 1);  /* flush and close intel hex file output */
573         return 0;
574 }
575
576
577 void print_usage(int fatal)
578 {
579   FILE *out = fatal ? stderr : stdout;
580
581   fprintf (out, "Usage: xa_asm [-s] [-v] file.xa\n");
582   fprintf (out, "  -v            verbose: show progress\n");
583   fprintf (out, "  -s            create symbol file\n");
584   fprintf (out, "  --version     show version/copyright info and exit\n");
585   fprintf (out, "  --help        show this and exit\n");
586 #if 0
587   // some usefull options I can think of.
588   fprintf (out, "  -m            create map file\n");
589   fprintf (out, "  -ss           create symbol file sorted by symbol\n");
590   fprintf (out, "  -sa           create symbol file sorted by segment/address\n");
591   fprintf (out, "  --no-temps    supress temp symbols in map and sym file\n");
592   fprintf (out, "  --code-loc=#  sets the start address of the code\n");
593   fprintf (out, "  --xdata-loc=# sets the start address of the external data\n");
594   fprintf (out, "  --stack-loc=# sets the start address of the stack\n");
595 #endif
596   exit(fatal);
597 }
598