update configure stuff
[xorcyst.git] / xasm.c
blob5c16473b048e3f9c6f7b4ab7fda4a0d4285327eb
1 /*
2 * $Id: xasm.c,v 1.22 2007/11/11 22:35:22 khansen Exp $
3 * $Log: xasm.c,v $
4 * Revision 1.22 2007/11/11 22:35:22 khansen
5 * compile on mac
7 * Revision 1.21 2007/08/19 11:18:56 khansen
8 * --case-insensitive option
10 * Revision 1.20 2007/08/12 18:58:12 khansen
11 * ability to generate pure 6502 binary (--pure-binary switch)
13 * Revision 1.19 2007/08/11 01:24:36 khansen
14 * includepaths support (-I option)
16 * Revision 1.18 2007/08/10 20:21:02 khansen
17 * *** empty log message ***
19 * Revision 1.17 2007/08/07 22:42:53 khansen
20 * version
22 * Revision 1.16 2007/07/22 14:49:40 khansen
23 * don't crash in change_extension()
25 * Revision 1.15 2007/07/22 13:33:26 khansen
26 * convert tabs to whitespaces
28 * Revision 1.14 2005/01/09 11:19:23 kenth
29 * xorcyst 1.4.5
31 * Revision 1.13 2005/01/05 09:37:32 kenth
32 * xorcyst 1.4.4
34 * Revision 1.12 2005/01/05 01:52:13 kenth
35 * xorcyst 1.4.3
37 * Revision 1.11 2005/01/04 21:35:10 kenth
38 * return error code from main() when error count > 0
40 * Revision 1.10 2004/12/29 21:43:50 kenth
41 * xorcyst 1.4.2
43 * Revision 1.9 2004/12/25 02:23:19 kenth
44 * xorcyst 1.4.1
46 * Revision 1.8 2004/12/19 19:58:46 kenth
47 * xorcyst 1.4.0
49 * Revision 1.7 2004/12/18 17:01:21 kenth
50 * --debug switch, multiple verbose levels
52 * Revision 1.6 2004/12/16 13:20:35 kenth
53 * xorcyst 1.3.5
55 * Revision 1.5 2004/12/14 01:50:12 kenth
56 * xorcyst 1.3.0
58 * Revision 1.4 2004/12/11 02:06:27 kenth
59 * xorcyst 1.2.0
61 * Revision 1.3 2004/12/06 04:53:02 kenth
62 * xorcyst 1.1.0
64 * Revision 1.2 2004/06/30 23:37:54 kenth
65 * replaced argp with something else
67 * Revision 1.1 2004/06/30 07:56:02 kenth
68 * Initial revision
72 /**
73 * (C) 2004 Kent Hansen
75 * The XORcyst is free software; you can redistribute it and/or modify
76 * it under the terms of the GNU General Public License as published by
77 * the Free Software Foundation; either version 2 of the License, or
78 * (at your option) any later version.
80 * The XORcyst is distributed in the hope that it will be useful,
81 * but WITHOUT ANY WARRANTY; without even the implied warranty of
82 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
83 * GNU General Public License for more details.
85 * You should have received a copy of the GNU General Public License
86 * along with The XORcyst; if not, write to the Free Software
87 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
90 /**
91 * The main program.
94 #include <stdlib.h>
95 #include <stdio.h>
96 #include <string.h>
97 #include <unistd.h>
98 #include "getopt.h"
99 #include "astnode.h"
100 #include "astproc.h"
101 #include "symtab.h"
102 #include "codegen.h"
103 #include "xasm.h"
105 /*---------------------------------------------------------------------------*/
107 /* Parser stuff we need. */
108 int yyparse(void);
109 extern int yydebug;
110 extern int yynerrs;
112 /* Scanner stuff we need. */
113 int yybegin(const char *, int, int);
115 /* Other. */
116 astnode *root_node;
117 static symtab *symbol_table;
118 char *xasm_path;
120 /*---------------------------------------------------------------------------*/
121 /* Argument parsing stuff. */
123 static char program_version[] = "xasm 1.5.1";
125 /* Argument variables set by arg parser. */
126 xasm_arguments xasm_args;
128 /* Long options for getopt_long(). */
129 static struct option long_options[] = {
130 { "define", required_argument, 0, 'D' },
131 { "include-path", required_argument, 0, 'I' },
132 { "output", required_argument, 0, 'o' },
133 { "quiet", no_argument, 0, 'q' },
134 { "silent", no_argument, 0, 's' },
135 { "verbose", no_argument, 0, 'v' },
136 { "debug", no_argument, 0, 'g' },
137 { "help", no_argument, 0, 0 },
138 { "usage", no_argument, 0, 0 },
139 { "version", no_argument, 0, 'V' },
140 { "swap-parens", no_argument, 0, 0 },
141 { "pure-binary", no_argument, 0, 0 },
142 { "case-insensitive", no_argument, 0, 0 },
143 { "no-warn", no_argument, 0, 0 },
144 { 0 }
147 /* Prints usage message and exits. */
148 static void usage()
150 printf("\
151 Usage: xasm [-gqsvV] [-D IDENT[=VALUE]] [--define=IDENT]\n\
152 [-o FILE] [--output=FILE] [--pure-binary]\n\
153 [--include-path=DIR] [-I DIR] [--swap-parens]\n\
154 [--case-insensitive]\n\
155 [--no-warn] [--verbose] [--quiet] [--silent] \n\
156 [--debug] [--help] [--usage] [--version]\n\
157 FILE\n\
159 exit(0);
162 /* Prints help message and exits. */
163 static void help()
165 printf("\
166 Usage: xasm [OPTION...] FILE\n\
167 The XORcyst Assembler -- it kicks the 6502's ass\n\
169 -D, --define=IDENT[=VALUE] Define IDENT\n\
170 -I, --include-path=DIR Specify a search path for include files\n\
171 -o, --output=FILE Output to FILE instead of standard output\n\
172 --pure-binary Output pure 6502 binary\n\
173 --swap-parens Use ( ) instead of [ ] for indirection\n\
174 --case-insensitive Case-insensitive identifiers\n\
175 --no-warn Suppress warnings\n\
176 -q, -s, --quiet, --silent Don't produce any output\n\
177 -v, --verbose Produce verbose output\n\
178 -g, --debug Retain file locations\n\
179 --help Give this help list\n\
180 --usage Give a short usage message\n\
181 -V, --version Print program version\n\
183 Mandatory or optional arguments to long options are also mandatory or optional\n\
184 for any corresponding short options.\n\
186 Report bugs to <dev@null>.\n\
188 exit(0);
191 /* Prints version and exits. */
192 static void version()
194 printf("%s\n", program_version);
195 exit(0);
199 * Checks if a character is alpha (a-z, A-Z).
201 static int __isalpha(char c)
203 return ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) );
207 * Checks if a character is alpha (a-z, A-Z) or numeric (0-9).
209 static int __isalnum(char c)
211 return ( __isalpha(c) || ((c >= '0') && (c <= '9')) );
215 * Checks that an identifier matches the regexp [a-zA-Z_][a-zA-Z0-9_]*
216 * @param id Identifier to validate
217 * @return 1 if OK, 0 otherwise
219 static int validate_ident(char *id)
221 int i;
222 char c;
223 c = id[0];
224 if ( !__isalpha(c) && (c != '_') ) {
225 return 0;
227 for (i=1; i<strlen(id); i++) {
228 c = id[i];
229 if ( !__isalnum(c) && (c != '_') ) {
230 return 0;
233 return 1; /* OK */
236 /* Parses program arguments. */
237 static void
238 parse_arguments (int argc, char **argv)
240 int key;
241 char *id;
242 char *str;
243 astnode *val;
244 symtab_entry *e;
245 /* Dummy location for --define */
246 static location loc = { 0, 0, 0, 0, NULL };
247 /* getopt_long stores the option index here. */
248 int index = 0;
250 /* Set default values. */
251 xasm_args.debug = 0;
252 xasm_args.silent = 0;
253 xasm_args.verbose = 0;
254 xasm_args.swap_parens = 0;
255 xasm_args.pure_binary = 0;
256 xasm_args.case_insensitive = 0;
257 xasm_args.input_file = NULL;
258 xasm_args.output_file = NULL;
259 xasm_args.include_paths = NULL;
260 xasm_args.include_path_count = 0;
262 /* Parse options. */
263 while ((key = getopt_long(argc, argv, "D:I:o:qsvV", long_options, &index)) != -1) {
264 switch (key) {
265 case 'g':
266 xasm_args.debug = 1;
267 break;
269 case 'q': case 's':
270 xasm_args.silent = 1;
271 break;
273 case 'v':
274 xasm_args.verbose++;
275 break;
277 case 'o':
278 xasm_args.output_file = optarg;
279 break;
281 case 'D':
282 if (strchr(optarg, '=') != NULL) {
283 /* IDENT=VALUE */
284 id = strtok(optarg, "=");
285 str = strtok(NULL, "\0");
286 /* Parse the value */
287 if (str[0] == '\"') {
288 /* Assume string */
289 str = strtok(&str[1], "\"");
290 val = astnode_create_string(str, loc);
291 } else {
292 /* Assume integer */
293 val = astnode_create_integer(strtol(str, NULL, 0), loc);
295 } else {
296 id = optarg;
297 val = astnode_create_integer(0, loc);
299 if (validate_ident(id)) {
300 e = symtab_lookup(id);
301 if (e == NULL) {
302 symtab_enter(id, CONSTANT_SYMBOL, val, 0);
303 } else {
304 /* Error, redefinition */
305 fprintf(stderr, "--ident: `%s' already defined\n", id);
307 } else {
308 /* Error, bad identifier */
309 fprintf(stderr, "--ident: `%s' is not a valid identifier\n", id);
311 break;
313 case 'I': {
314 char *p;
315 int count = xasm_args.include_path_count + 1;
316 xasm_args.include_paths = (char **)realloc(
317 xasm_args.include_paths, sizeof(const char *) * count);
318 p = (char *)malloc(strlen(optarg) + 1);
319 strcpy(p, optarg);
320 xasm_args.include_paths[count-1] = p;
321 xasm_args.include_path_count = count;
323 break;
325 case 0:
326 /* Use index to differentiate between options */
327 if (strcmp(long_options[index].name, "usage") == 0) {
328 usage();
329 } else if (strcmp(long_options[index].name, "help") == 0) {
330 help();
331 } else if (strcmp(long_options[index].name, "swap-parens") == 0) {
332 xasm_args.swap_parens = 1;
333 } else if (strcmp(long_options[index].name, "pure-binary") == 0) {
334 xasm_args.pure_binary = 1;
335 } else if (strcmp(long_options[index].name, "case-insensitive") == 0) {
336 xasm_args.case_insensitive = 1;
337 } else if (strcmp(long_options[index].name, "no-warn") == 0) {
338 xasm_args.no_warn = 1;
340 break;
342 case 'V':
343 version();
344 break;
346 case '?':
347 /* Error message has been printed by getopt_long */
348 exit(1);
349 break;
351 default:
352 /* Forgot to handle a short option, most likely */
353 exit(1);
354 break;
358 /* Must be one additional argument, which is the input file. */
359 if (argc-1 != optind) {
360 printf("Usage: xasm [OPTION...] FILE\nTry `xasm --help' or `xasm --usage' for more information.\n");
361 exit(1);
363 else {
364 xasm_args.input_file = argv[optind];
368 /*---------------------------------------------------------------------------*/
371 * Changes the extension of a filename.
372 * @param infile Filename whose extension to change
373 * @param ext New extension
374 * @param outfile Destination filename
376 static void change_extension(const char *infile, const char *ext, char *outfile)
378 char *p;
379 /* Find the last dot. */
380 p = strrchr(infile, '.');
381 if (p == NULL) {
382 /* There is no dot, simply concatenate extension. */
383 sprintf(outfile, "%s.%s", infile, ext);
385 else {
386 /* Copy the name up to and including the last dot */
387 strncpy(outfile, infile, p - infile + 1);
388 outfile[p - infile + 1] = '\0';
389 /* Then concatenate the extension. */
390 strcat(outfile, ext);
394 /*---------------------------------------------------------------------------*/
397 * Prints message only if --verbose option was given to assembler.
399 static void verbose(const char *s)
401 if (xasm_args.verbose) {
402 printf("%s\n", s);
407 * Gets total number of errors (parsing + semantics).
409 static int total_errors()
411 return yynerrs + astproc_err_count();
415 * Program entrypoint.
417 int main(int argc, char *argv[]) {
418 char *default_outfile = 0;
420 /* Working directory is needed for include statements */
421 xasm_path = getcwd(NULL, 0);
423 /* Create global symbol table (auto-pushed on stack) */
424 symbol_table = symtab_create();
426 /* Parse our arguments. */
427 parse_arguments (argc, argv);
429 /* Open input for scanning */
430 if (!yybegin(xasm_args.input_file,
431 xasm_args.swap_parens,
432 xasm_args.case_insensitive)) {
433 printf("error: could not open `%s' for reading\n", xasm_args.input_file);
434 symtab_finalize(symbol_table);
435 return(1);
438 /* Parse it into a syntax tree */
439 //yydebug = -1;
440 verbose("Parsing input...");
441 yyparse();
443 if (root_node == NULL) {
444 symtab_finalize(symbol_table);
445 return(0);
448 /* First pass does a lot of stuff. */
449 verbose("First pass...");
450 astproc_first_pass(root_node);
452 /* Second pass does more stuff. */
453 verbose("Second pass...");
454 astproc_second_pass(root_node);
456 /* Third pass is fun. */
457 verbose("Third pass...");
458 astproc_third_pass(root_node);
460 if (xasm_args.pure_binary) {
461 /* Do another pass to prepare for writing pure 6502 */
462 verbose("Fourth pass...");
463 astproc_fourth_pass(root_node);
466 /* Print the final AST (debugging) */
467 // astnode_print(root_node, 0);
469 /* If no errors, proceed with code generation. */
470 if (total_errors() == 0) {
471 if (xasm_args.output_file == NULL) {
472 /* Create default name of output */
473 const char *default_ext = "o";
474 int default_outfile_len = strlen(xasm_args.input_file)
475 + /*dot*/1 + strlen(default_ext) + 1;
476 default_outfile = (char *)malloc(default_outfile_len);
477 change_extension(xasm_args.input_file, default_ext, default_outfile);
478 xasm_args.output_file = default_outfile;
480 /* Write it! */
481 verbose("Generating final output...");
482 if (xasm_args.pure_binary) {
483 astproc_fifth_pass(root_node);
484 } else {
485 codegen_write(root_node, xasm_args.output_file);
489 /* Cleanup */
490 verbose("cleaning up...");
491 symtab_pop();
492 symtab_finalize(symbol_table);
493 astnode_finalize(root_node);
495 if (default_outfile)
496 free(default_outfile);
498 if (xasm_args.include_path_count > 0) {
499 int i;
500 for (i = 0; i < xasm_args.include_path_count; ++i)
501 free(xasm_args.include_paths[i]);
502 free(xasm_args.include_paths);
505 free(xasm_path);
507 return (total_errors() == 0) ? 0 : 1;