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