2 * $Id: xasm.c,v 1.22 2007/11/11 22:35:22 khansen Exp $
4 * Revision 1.22 2007/11/11 22:35:22 khansen
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
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
31 * Revision 1.13 2005/01/05 09:37:32 kenth
34 * Revision 1.12 2005/01/05 01:52:13 kenth
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
43 * Revision 1.9 2004/12/25 02:23:19 kenth
46 * Revision 1.8 2004/12/19 19:58:46 kenth
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
55 * Revision 1.5 2004/12/14 01:50:12 kenth
58 * Revision 1.4 2004/12/11 02:06:27 kenth
61 * Revision 1.3 2004/12/06 04:53:02 kenth
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
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
104 /*---------------------------------------------------------------------------*/
106 /* Parser stuff we need. */
111 /* Scanner stuff we need. */
112 int yybegin(const char *, int, int);
116 static symtab
*symbol_table
;
118 /*---------------------------------------------------------------------------*/
119 /* Argument parsing stuff. */
121 static char program_version
[] = "xasm 1.5.0";
123 /* Argument variables set by arg parser. */
124 xasm_arguments xasm_args
;
126 /* Long options for getopt_long(). */
127 static struct option long_options
[] = {
128 { "define", required_argument
, 0, 'D' },
129 { "include-path", required_argument
, 0, 'I' },
130 { "output", required_argument
, 0, 'o' },
131 { "quiet", no_argument
, 0, 'q' },
132 { "silent", no_argument
, 0, 's' },
133 { "verbose", no_argument
, 0, 'v' },
134 { "debug", no_argument
, 0, 'g' },
135 { "help", no_argument
, 0, 0 },
136 { "usage", no_argument
, 0, 0 },
137 { "version", no_argument
, 0, 'V' },
138 { "swap-parens", no_argument
, 0, 0 },
139 { "pure-binary", no_argument
, 0, 0 },
140 { "case-insensitive", no_argument
, 0, 0 },
141 { "no-warn", no_argument
, 0, 0 },
145 /* Prints usage message and exits. */
149 Usage: xasm [-gqsvV] [-D IDENT[=VALUE]] [--define=IDENT]\n\
150 [-o FILE] [--output=FILE] [--pure-binary]\n\
151 [--include-path=DIR] [-I DIR] [--swap-parens]\n\
152 [--case-insensitive]\n\
153 [--no-warn] [--verbose] [--quiet] [--silent] \n\
154 [--debug] [--help] [--usage] [--version]\n\
160 /* Prints help message and exits. */
164 Usage: xasm [OPTION...] FILE\n\
165 The XORcyst Assembler -- it kicks the 6502's ass\n\
167 -D, --define=IDENT[=VALUE] Define IDENT\n\
168 -I, --include-path=DIR Specify a search path for include files\n\
169 -o, --output=FILE Output to FILE instead of standard output\n\
170 --pure-binary Output pure 6502 binary\n\
171 --swap-parens Use ( ) instead of [ ] for indirection\n\
172 --case-insensitive Case-insensitive identifiers\n\
173 --no-warn Suppress warnings\n\
174 -q, -s, --quiet, --silent Don't produce any output\n\
175 -v, --verbose Produce verbose output\n\
176 -g, --debug Retain file locations\n\
177 --help Give this help list\n\
178 --usage Give a short usage message\n\
179 -V, --version Print program version\n\
181 Mandatory or optional arguments to long options are also mandatory or optional\n\
182 for any corresponding short options.\n\
184 Report bugs to <dev@null>.\n\
189 /* Prints version and exits. */
190 static void version()
192 printf("%s\n", program_version
);
197 * Checks if a character is alpha (a-z, A-Z).
199 static int __isalpha(char c
)
201 return ( ((c
>= 'a') && (c
<= 'z')) || ((c
>= 'A') && (c
<= 'Z')) );
205 * Checks if a character is alpha (a-z, A-Z) or numeric (0-9).
207 static int __isalnum(char c
)
209 return ( __isalpha(c
) || ((c
>= '0') && (c
<= '9')) );
213 * Checks that an identifier matches the regexp [a-zA-Z_][a-zA-Z0-9_]*
214 * @param id Identifier to validate
215 * @return 1 if OK, 0 otherwise
217 static int validate_ident(char *id
)
222 if ( !__isalpha(c
) && (c
!= '_') ) {
225 for (i
=1; i
<strlen(id
); i
++) {
227 if ( !__isalnum(c
) && (c
!= '_') ) {
234 /* Parses program arguments. */
236 parse_arguments (int argc
, char **argv
)
243 /* Dummy location for --define */
244 static location loc
= { 0, 0, 0, 0, NULL
};
245 /* getopt_long stores the option index here. */
248 /* Set default values. */
250 xasm_args
.silent
= 0;
251 xasm_args
.verbose
= 0;
252 xasm_args
.swap_parens
= 0;
253 xasm_args
.pure_binary
= 0;
254 xasm_args
.case_insensitive
= 0;
255 xasm_args
.input_file
= NULL
;
256 xasm_args
.output_file
= NULL
;
257 xasm_args
.include_paths
= NULL
;
258 xasm_args
.include_path_count
= 0;
261 while ((key
= getopt_long(argc
, argv
, "D:I:o:qsvV", long_options
, &index
)) != -1) {
268 xasm_args
.silent
= 1;
276 xasm_args
.output_file
= optarg
;
280 if (strchr(optarg
, '=') != NULL
) {
282 id
= strtok(optarg
, "=");
283 str
= strtok(NULL
, "\0");
284 /* Parse the value */
285 if (str
[0] == '\"') {
287 str
= strtok(&str
[1], "\"");
288 val
= astnode_create_string(str
, loc
);
291 val
= astnode_create_integer(strtol(str
, NULL
, 0), loc
);
295 val
= astnode_create_integer(0, loc
);
297 if (validate_ident(id
)) {
298 e
= symtab_lookup(id
);
300 symtab_enter(id
, CONSTANT_SYMBOL
, val
, 0);
302 /* Error, redefinition */
303 fprintf(stderr
, "--ident: `%s' already defined\n", id
);
306 /* Error, bad identifier */
307 fprintf(stderr
, "--ident: `%s' is not a valid identifier\n", id
);
313 int count
= xasm_args
.include_path_count
+ 1;
314 xasm_args
.include_paths
= (char **)realloc(
315 xasm_args
.include_paths
, sizeof(const char *) * count
);
316 p
= (char *)malloc(strlen(optarg
) + 1);
318 xasm_args
.include_paths
[count
-1] = p
;
319 xasm_args
.include_path_count
= count
;
324 /* Use index to differentiate between options */
325 if (strcmp(long_options
[index
].name
, "usage") == 0) {
327 } else if (strcmp(long_options
[index
].name
, "help") == 0) {
329 } else if (strcmp(long_options
[index
].name
, "swap-parens") == 0) {
330 xasm_args
.swap_parens
= 1;
331 } else if (strcmp(long_options
[index
].name
, "pure-binary") == 0) {
332 xasm_args
.pure_binary
= 1;
333 } else if (strcmp(long_options
[index
].name
, "case-insensitive") == 0) {
334 xasm_args
.case_insensitive
= 1;
335 } else if (strcmp(long_options
[index
].name
, "no-warn") == 0) {
336 xasm_args
.no_warn
= 1;
345 /* Error message has been printed by getopt_long */
350 /* Forgot to handle a short option, most likely */
356 /* Must be one additional argument, which is the input file. */
357 if (argc
-1 != optind
) {
358 printf("Usage: xasm [OPTION...] FILE\nTry `xasm --help' or `xasm --usage' for more information.\n");
362 xasm_args
.input_file
= argv
[optind
];
366 /*---------------------------------------------------------------------------*/
369 * Changes the extension of a filename.
370 * @param infile Filename whose extension to change
371 * @param ext New extension
372 * @param outfile Destination filename
374 static void change_extension(const char *infile
, const char *ext
, char *outfile
)
377 /* Find the last dot. */
378 p
= strrchr(infile
, '.');
380 /* There is no dot, simply concatenate extension. */
381 sprintf(outfile
, "%s.%s", infile
, ext
);
384 /* Copy the name up to and including the last dot */
385 strncpy(outfile
, infile
, p
- infile
+ 1);
386 outfile
[p
- infile
+ 1] = '\0';
387 /* Then concatenate the extension. */
388 strcat(outfile
, ext
);
392 /*---------------------------------------------------------------------------*/
395 * Prints message only if --verbose option was given to assembler.
397 static void verbose(const char *s
)
399 if (xasm_args
.verbose
) {
405 * Gets total number of errors (parsing + semantics).
407 static int total_errors()
409 return yynerrs
+ astproc_err_count();
413 * Program entrypoint.
415 int main(int argc
, char *argv
[]) {
416 char *default_outfile
= 0;
418 /* Create global symbol table (auto-pushed on stack) */
419 symbol_table
= symtab_create();
421 /* Parse our arguments. */
422 parse_arguments (argc
, argv
);
424 /* Open input for scanning */
425 if (!yybegin(xasm_args
.input_file
,
426 xasm_args
.swap_parens
,
427 xasm_args
.case_insensitive
)) {
428 printf("error: could not open `%s' for reading\n", xasm_args
.input_file
);
429 symtab_finalize(symbol_table
);
433 /* Parse it into a syntax tree */
435 verbose("Parsing input...");
438 if (root_node
== NULL
) {
439 symtab_finalize(symbol_table
);
443 /* First pass does a lot of stuff. */
444 verbose("First pass...");
445 astproc_first_pass(root_node
);
447 /* Second pass does more stuff. */
448 verbose("Second pass...");
449 astproc_second_pass(root_node
);
451 /* Third pass is fun. */
452 verbose("Third pass...");
453 astproc_third_pass(root_node
);
455 if (xasm_args
.pure_binary
) {
456 /* Do another pass to prepare for writing pure 6502 */
457 verbose("Fourth pass...");
458 astproc_fourth_pass(root_node
);
461 /* Print the final AST (debugging) */
462 // astnode_print(root_node, 0);
464 /* If no errors, proceed with code generation. */
465 if (total_errors() == 0) {
466 if (xasm_args
.output_file
== NULL
) {
467 /* Create default name of output */
468 const char *default_ext
= "o";
469 int default_outfile_len
= strlen(xasm_args
.input_file
)
470 + /*dot*/1 + strlen(default_ext
) + 1;
471 default_outfile
= (char *)malloc(default_outfile_len
);
472 change_extension(xasm_args
.input_file
, default_ext
, default_outfile
);
473 xasm_args
.output_file
= default_outfile
;
476 verbose("Generating final output...");
477 if (xasm_args
.pure_binary
) {
478 astproc_fifth_pass(root_node
);
480 codegen_write(root_node
, xasm_args
.output_file
);
485 verbose("cleaning up...");
487 symtab_finalize(symbol_table
);
488 astnode_finalize(root_node
);
491 free(default_outfile
);
493 if (xasm_args
.include_path_count
> 0) {
495 for (i
= 0; i
< xasm_args
.include_path_count
; ++i
)
496 free(xasm_args
.include_paths
[i
]);
497 free(xasm_args
.include_paths
);
500 return (total_errors() == 0) ? 0 : 1;