/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

/*
 * make a 'prom' from a collection of as-assembled files.
 * The first arg is the size of the prom in bytes.
 * The other arguments should be triples of relative load
 * address, offset and filename.
 * The call
 *  mkrom 1024 0 17300 file1.s11 1000 165000 file2.s11
 * loads the files file1.s11 and file2.s11 into the rom.
 * file1 is loaded starting at rom position 0, 173000
 * is subtracted from the load address in the file. file2 gets
 * loaded starting at 1000 and 165000 is subtracted.
 * This means, that given a triple <L O F> if a byte is located
 * at position P in F, it will go into the ROM at address
 * (P - O) + L.
 */

# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>
# ifdef HAVE_GETOPT_H
#  include <getopt.h>
# endif
# include <begemot.h>
# include "cdefs.h"
# include "util.h"

RCSID("$Id: mkrom.c 476 2001-07-09 08:09:10Z hbb $")

typedef struct obj_t obj_t;
struct obj_t {
	obj_t	*next;		/* link object files */
	char	*fname;		/* file name */
	u_int	base;		/* load base */
	u_int	reloc;		/* relocation offset */
	char	code;		/* occupation code */
};

u_char	*rom;
obj_t	**occ;
u_int	size = 2*8192;
char	*outfile, *mapfile;
obj_t	*objs;
obj_t	**eobjs = &objs;
u_int	errcnt;

static void loadfile(obj_t *, FILE *);
static void load(void);
static void usage(void) DEAD_FUNC;
static void domap(void);
static void dorom(void);

char usgtxt[] =
"Usage: mkrom [-s size] [-o outfile] [-m mapfile] {[-r offs] [-b base] file}*\n"
"Options:\n"
"	-s size		set ROM size in bytes\n"
"	-o outfile	set output file name\n"
"	-m mapfile	set map file name\n"
"	-r offs		set relocation offset for next files\n"
"	-b base		set load base for next files\n"
"	-h		print this info\n"
"The load base is incremented by 01000 after each file is processed.\n";

int
main(int argc, char *argv[])
{
	u_int reloc = 0, base = 0;
	obj_t *o;
	char *end;
	int opt, nfile = 0;

	set_argv0(argv[0]);

	while((opt = getopt(argc, argv, "hs:o:m:r:b:")) != EOF)
		switch(opt) {

		  case 'h':
			usage();

		  case 'o':
			outfile = optarg;
			break;

		  case 'm':
			mapfile = optarg;
			break;

		  case 's':
			size = (u_int)strtoul(optarg, &end, 0);
			if(*end != '\0')
				panic("bad size '%s'", optarg);
			if(size == 0)
				panic("zero prom size");
			break;

		  case 'r':
			reloc = (u_int)strtoul(optarg, &end, 0);
			if(*end != '\0')
				panic("bad reloc base '%s'", optarg);
			break;

		  case 'b':
			base = (u_int)strtoul(optarg, &end, 0);
			if(*end != '\0')
				panic("bad load base '%s'", optarg);
			break;
		}

	while(optind < argc) {
		nfile++;

		o = xalloc(sizeof(obj_t));

		o->next = NULL;
		o->fname = argv[optind++];
		o->reloc = reloc;
		o->base = base;

		*eobjs = o;
		eobjs = &o->next;

		base += 01000;

		while((opt = getopt(argc, argv, "r:b:")) != EOF)
			switch(opt) {

			  case 'r':
				reloc = (u_int)strtoul(optarg, &end, 0);
				if(*end != '\0')
					panic("bad reloc base '%s'", optarg);
				break;

			  case 'b':
				base = (u_int)strtoul(optarg, &end, 0);
				if(*end != '\0')
					panic("bad load base '%s'", optarg);
				break;
			}
	}

	if(nfile == 0)
		usage();

	rom = xalloc(size);
	occ = xalloc(sizeof(obj_t *) * size);
	(void)memset(rom, 0, size);
	(void)memset(occ, 0, sizeof(obj_t *) * size);

	load();

	if(mapfile)
		domap();

	if(outfile)
		dorom();

	if(errcnt != 0)
		fprintf(stderr, "%d errors detected\n", errcnt);

	return errcnt != 0;
}

static void
usage()
{
	fprintf(stderr, usgtxt);
	exit(1);
}

static void
domap()
{
	FILE *fp;
	obj_t *o;
	u_int n, a;
	int c;

	if(strcmp(mapfile, "-") == 0)
		fp = stdout;
	else if((fp = fopen(mapfile, "w")) == NULL)
		panic("can't open mapfile %s: %s", mapfile, strerror(errno));

	fprintf(fp, "ROM size: %6o bytes\n", size);
	fprintf(fp, "\n");

	fprintf(fp, "List of loaded files:\n");
	fprintf(fp, "no  code file                 base   reloc\n");
	fprintf(fp, "--  ---- -------------------- ------ ------\n");
	for(o = objs, n = 0; o != NULL; o = o->next, n++) {
		o->code = 'a' + n;
		fprintf(fp, "%2d: %-4c %-20s %06o %06o\n", n, o->code,
			o->fname, o->base, o->reloc);
	}
	fprintf(fp, "\n");

	fprintf(fp, "ROM occupation:\n");
	for(a = 0; a < size; a++) {
		c = (occ[a] ? occ[a]->code : '.');
		if(a % 64 == 0) {
			if(a != 0)
				fprintf(fp, "\n");
			fprintf(fp, "%06o ", a);
		} else if(a % 8 == 0)
			fprintf(fp, " ");
		fprintf(fp, "%c", c);
	}
	fprintf(fp, "\n\n");

	fprintf(fp, "Load errors: %d\n", errcnt);

	if(strcmp(mapfile, "-") != 0)
		fclose(fp);
}

static void
dorom()
{
	FILE *fp;
	int n;

	if(strcmp(outfile, "-") == 0)
		fp = stdout;
	else if((fp = fopen(outfile, "w")) == NULL)
		panic("can't open outfile %s: %s", outfile, strerror(errno));

	if((n = fwrite(rom, 1, size, fp)) == 0)
		panic("outfile write error: %s", strerror(errno));
	if((u_int)n != size)
		panic("short write on outfile");

	if(strcmp(outfile, "-") != 0)
		fclose(fp);
}

static void
load()
{
	FILE *fp;
	obj_t *o;

	for(o = objs; o != NULL; o = o->next) {
		if((fp = fopen(o->fname, "r")) == NULL)
			panic("%s: %s", o->fname, strerror(errno));
		loadfile(o, fp);
		fclose(fp);
	}
}

static void
loadfile(obj_t *o, FILE *fp)
{
	u_int a;	/* original load address */
	u_int l;	/* number of bytes */
	u_int b;	/* byte */
	u_int p;	/* effective load address */

	while(fscanf(fp, "%x %x", &a, &l) == 2) {
		if(a < o->reloc) {
			fprintf(stderr, "%s: reloc offset %o > load addr %o\n",
				o->fname, o->reloc, a);
			errcnt++;
			goto skip;
		}
		while(l--) {
			p = o->base + a - o->reloc;
			if(p >= size) {
				fprintf(stderr, "%s: load address to big "
					"%o(%o)\n", o->fname, a, p);
				errcnt++;
				goto skip;
			}
			if(fscanf(fp, "%x", &b) != 1)
				panic("%s: format error\n", o->fname);
			if(occ[p]) {
				fprintf(stderr, "%s: address already occupied "
					"%o(%o)\n", o->fname, a, p);
				errcnt++;
				goto skip;
			}
			occ[p] = o;
			rom[p] = b;
			a++;
		}
		continue;
  skip:
		while(l--) {
			if(fscanf(fp, "%x", &b) != 1)
				panic("%s: format error\n", o->fname);
		}
	}
	if(!feof(fp))
		panic("%s: format error\n", o->fname);
}
