
/*
 * load.c
 *
 * (C) Copyright 1995 Archie L. Cobbs
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "error.h"
#include "names.h"
#include "obj.h"
#include "load.h"

/*
 * DEFINITIONS
 */

	#define currArg		(ac ? *av : NULL)
	#define nextArg		((ac && --ac) ? *++av : NULL)

/*
 * GLOBAL VARIABLES
 */

	char				*gProgName = "a2load";
	char				*gUsageMsg = "[-v] [-e] [-base addr] file";

	char				*gFileName;
	int					gLineNumber = 0;
	int					gVerbose = FALSE;

	Byte				*gBuffer;
	unsigned			gBase[NUM_SEGMENTS];
	struct ObjHeader	gHeader;
	Names				gNames;
	int					gExec = FALSE;

/*
 * INTERNAL FUNCTIONS
 */

	static	void		LoadSegment(FILE *fp, Byte *buf, int size);
	static	void		LoadSymbols(FILE *fp, int num);
	static	void		WriteBinary(FILE *fp, Byte *buf, int size);
	static	void		WriteExec(FILE *fp, Byte *buf, unsigned base, int size);

/*
 *	main()
 */

int
main(int ac, char *av[])
{
	FILE	*infp;
	FILE	*outfp = stdout;
	int		name;

/* Default load location */

	gBase[CodeSegment] = DEFAULT_LOAD_BASE;

/* Get command line paramters	*/

	while (nextArg && *currArg == '-')
		if (!strcmp(currArg, "-v"))
			gVerbose = TRUE;
		else if (!strcmp(currArg, "-base"))
		{
			if (!nextArg)
				Usage("missing argument");
			gBase[CodeSegment] = atoi(currArg);
		}
		else if (!strcmp(currArg, "-e"))
			gExec = TRUE;
		else
			Usage(NULL);
	if (ac != 1)
		Usage(NULL);
	if ((infp = fopen(gFileName = currArg, "r")) == NULL)
	{
		perror("fopen");
		cerror("can't open file");
	}

/* Read header */

	if (fread(&gHeader, sizeof(struct ObjHeader), 1, infp) != 1)
	{
		if (ferror(infp))
			perror("fread");
		cerror("can't read header");
	}
	if (gHeader.n_magic != OBJ_HEADER_MAGIC)
		cerror("wrong object file format");

/* Set base addresses */

	gBase[DataSegment] = gBase[CodeSegment] + gHeader.n_text;
	gBase[BssSegment] = gBase[DataSegment] + gHeader.n_data;
	gBase[AbsSegment] = 0;

/* Allocate buffer */

	gBuffer = GetMem(gHeader.n_text + gHeader.n_data);

/* Load segments */

	LoadSegment(infp, gBuffer, gHeader.n_text);
	LoadSegment(infp, gBuffer + gHeader.n_text, gHeader.n_data);

/* Load symbols */

	fseek(infp, gHeader.n_treloc * sizeof(struct ObjReloc), SEEK_CUR);
	fseek(infp, gHeader.n_dreloc * sizeof(struct ObjReloc), SEEK_CUR);

	LoadSymbols(infp, gHeader.n_symbols);

/* Load strings database */

	gNames = NamesLoad(infp, gHeader.n_strings);

/* Do relocations */

	fseek(infp, sizeof(struct ObjHeader), SEEK_SET);
	fseek(infp, gHeader.n_text, SEEK_CUR);
	fseek(infp, gHeader.n_data, SEEK_CUR);

	DoRelocs(infp, gBuffer, gHeader.n_treloc);
	DoRelocs(infp, gBuffer + gHeader.n_text, gHeader.n_dreloc);

/* Close input file */

	fclose(infp);

/* Report unknown symbols */

	while ((name = NextUnknown()) >= 0)
		uerror("symbol \"%s\" undefined", Name(gNames, (unsigned) name));

/* Abort if any errors */

	if (gNumErrors)
		cerror("errors prevent successful load");

/* Write merged and linked segments */

	if (gExec)
		WriteExec(outfp, gBuffer, gBase[CodeSegment], gHeader.n_text + gHeader.n_data);
	else
		WriteBinary(outfp, gBuffer, gHeader.n_text + gHeader.n_data);

/* Done */

	fclose(outfp);
	return(0);
}

/*
 * LoadSegment()
 */

static	void
LoadSegment(FILE *fp, Byte *buf, int size)
{
	if (fread(buf, 1, size, fp) != size)
	{
		if (ferror(fp))
			perror("fread");
		cerror("can't read segment");
	}
}

/*
 * LoadSymbols()
 */

static	void
LoadSymbols(FILE *fp, int num)
{
	int					k;
	struct ObjSymbol	symbol;
	Symbol				sym;

	for (k = 0; k < num; k++)
	{

	/* Read in next symbol */

		if (fread(&symbol, sizeof(struct ObjSymbol), 1, fp) != 1)
		{
			if (ferror(fp))
				perror("fread");
			cerror("can't read symbol");
		}

	/* Add to symbol table */

		sym = DefineSym(symbol.s_name);
		sym->segment = symbol.s_segment;
		sym->offset = symbol.s_offset;
	}
}

/*
 * WriteSegment()
 */

static	void
WriteBinary(FILE *fp, Byte *buf, int size)
{
	if (fwrite(buf, 1, size, fp) != size)
	{
		if (ferror(fp))
			perror("fwrite");
		cerror("can't write segment");
	}
}

/*
 * WriteExec()
 */

static	void
WriteExec(FILE *fp, Byte *buf, unsigned base, int size)
{
	int		offset;

	fprintf(fp, "CALL -151\n");

	for (offset = 0; offset < size; offset++)
	{
		if (offset % EXEC_CHUNK_SIZE == 0)
			fprintf(fp, "%X:", base + offset);
		fprintf(fp, "%02X ", buf[offset]);
		if ((offset + 1) % EXEC_CHUNK_SIZE == 0)
			fprintf(fp, "\n");
	}
	fprintf(fp, "\n");
}

