view lwasm/main.c @ 261:c79b3c88adbc 2.x

Added --depend option to generate a list of dependencies
author lost
date Sat, 26 Dec 2009 08:24:35 +0000
parents e27279180a73
children
line wrap: on
line source

/*
main.c
Copyright © 2008 William Astle

This file is part of LWASM.

LWASM is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.


Implements the program startup code

*/

#include <config.h>

#include <argp.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "lwasm.h"
#include "util.h"

// external declarations
extern void lwasm_pass1(asmstate_t *as);
extern void lwasm_pass2(asmstate_t *as);
extern void lwasm_list(asmstate_t *as);
extern void lwasm_output(asmstate_t *as);
extern void pseudo_pragma_real(asmstate_t *as, lwasm_line_t *cl, char **optr, int error);

// command line option handling
const char *argp_program_version = "LWASM from " PACKAGE_STRING;
const char *argp_program_bug_address = PACKAGE_BUGREPORT;

static error_t parse_opts(int key, char *arg, struct argp_state *state)
{
	asmstate_t *as = state -> input;
	char *p;
	
	switch (key)
	{
	case 'I':
		as -> includedirs = lwasm_realloc(as -> includedirs, (as -> nincludedirs + 1) * sizeof(char *));
		as -> includedirs[as -> nincludedirs++] = arg;
		break;

	case 'o':
		// output
		if (as -> outfile)
		{
		}
		as -> outfile = arg;
		break;
	
	case 'd':
		// debug
		debug_level++;
		break;
	
	case 'l':
		// list
		if (arg)
			as -> listfile = arg;
		else
			as -> listfile = "-";
		break;
	
	case 'b':
		// decb output
		as -> outformat = OUTPUT_DECB;
		break;
	
	case 'r':
		// raw binary output
		as -> outformat = OUTPUT_RAW;
		break;
	
	case 0x100:
		// proprietary object format
		as -> outformat = OUTPUT_OBJ;
		break;
	
	case 0x101:
		// dependency tracking
		as -> deptrack = 1;
		break;
		
	case 'f':
		// output format
		if (!strcasecmp(arg, "decb"))
			as -> outformat = OUTPUT_DECB;
		else if (!strcasecmp(arg, "raw"))
			as -> outformat = OUTPUT_RAW;
		else if (!strcasecmp(arg, "obj"))
			as -> outformat = OUTPUT_OBJ;
		else if (!strcasecmp(arg, "os9"))
		{
			as -> pragmas |= PRAGMA_DOLLARNOTLOCAL;
			as -> outformat = OUTPUT_OS9;
		}
		else
		{
			fprintf(stderr, "Invalid output format: %s\n", arg);
			exit(1);
		}
		break;
	
	case 'p':
		// pragmas
		p = arg;
		pseudo_pragma_real(as, NULL, &p, 2);
		if (!p)
		{
			fprintf(stderr, "Invalid pragma string: %s\n", arg);
			exit(1);
		}
		break;

	case '9':
		as -> no6309 = 1;
		break;
	
	case '3':
		as -> no6309 = 0;
		break;

	case ARGP_KEY_END:
		// done; sanity check
		if (!as -> outfile)
			as -> outfile = "a.out";
		break;
	
	case ARGP_KEY_ARG:
		// non-option arg
		if (as -> infile)
			argp_usage(state);
		as -> infile = arg;
		break;
		
	default:
		return ARGP_ERR_UNKNOWN;
	}
	return 0;
}

static struct argp_option options[] =
{
	{ "output",		'o',	"FILE",	0,
				"Output to FILE"},
	{ "debug",		'd',	0,		0,
				"Set debug mode"},
	{ "format",		'f',	"TYPE",	0,
				"Select output format: decb, raw, obj, os9"},
	{ "list",		'l',	"FILE",	OPTION_ARG_OPTIONAL,
				"Generate list [to FILE]"},
	{ "decb",		'b',	0,		0,
				"Generate DECB .bin format output, equivalent of --format=decb"},
	{ "raw",		'r',	0,		0,
				"Generate raw binary format output, equivalent of --format=raw"},
	{ "obj",		0x100,		0,		0,
				"Generate proprietary object file format for later linking, equivalent of --format=obj" },
	{ "depend",		0x101,		0,		0,
				"Output a dependency list to stdout; do not do any actual output though assembly is completed as usual" },
	{ "pragma",		'p',	"PRAGMA",	0,
				"Set an assembler pragma to any value understood by the \"pragma\" pseudo op"},
	{ "6809",		'9',	0,			0,
				"Set assembler to 6809 only mode" },
	{ "6309",		'3',	0,			0,
				"Set assembler to 6309 mode (default)" },
	{ "includedir",	'I',	"PATH",			0,
				"Add entry to include path" },
	{ 0 }
};

static struct argp argp =
{
	options,
	parse_opts,
	"<input file>",
	"LWASM, a HD6309 and MC6809 cross-assembler"
};

char *program_name;

// main function; parse command line, set up assembler state, and run the
// assembler on the first file
int main(int argc, char **argv)
{

	program_name = argv[0];
	
	// assembler state
	asmstate_t asmstate = { 0 };

	argp_parse(&argp, argc, argv, 0, 0, &asmstate);

	if (!asmstate.infile)
	{
		fprintf(stderr, "No input files specified.\n");
		exit(1);
	}	

	// lose the output file if it already exists...
	unlink(asmstate.outfile);

	/* pass 1 - collect the symbols and assign addresses where possible */
	/* pass 1 also resolves included files, etc. */
	/* that means files are read exactly once unless included multiple times */
	lwasm_pass1(&asmstate);
	
	// pass 2: actually generate the code; if any phasing errors appear
	// at this stage, we have a bug
	lwasm_pass2(&asmstate);

	// now make a pretty listing
	lwasm_list(&asmstate);

	// now write the code out to the output file
	if (asmstate.deptrack)
	{
		int i;
		// list the files read (on includes)
		for (i = 0; i < asmstate.nincfiles; i++)
		{
			printf("%s\n", asmstate.incfiles[i]);
		}
	}
	else
	{
		lwasm_output(&asmstate);
	}

	if (asmstate.errorcount > 0)
		exit(1);

	exit(0);
}