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

#include <stdio.h>
#include <assert.h>
#include "util.h"
#include "error.h"
#include "names.h"
#include "obj.h"
#include "asm.h"

/*
 * INTERNAL VARIABLES
 */

	static	int				gNumCodeRelocs;
	static	int				gNumDataRelocs;

	static	struct ObjReloc	gRelocCode[MAX_RELOCATIONS];
	static	struct ObjReloc	gRelocData[MAX_RELOCATIONS];

/*
 * INTERNAL FUNCTIONS
 */

	static	struct ObjReloc	*Reloc(unsigned name,
								int offset, int bige, int bytes);

/*
 * RelocSeg()
 *
 * Make a segment-relative relocation entry.
 */

void
RelocSeg(SegRel segrel, int bigendian, int bytes)
{
	struct ObjReloc	*reloc;

	(reloc = Reloc(segrel->segment,
		segrel->offset, bigendian, bytes))->r_segrel = TRUE;

	if (gDebug)
	{
		fprintf(stderr, "RELOC at %c $%04X : %cBASE + $%X "
						"(bytes=$%X bige=%d)\n",
			gSegment == CodeSegment ? 'C' :
			gSegment == DataSegment ? 'D' : 'B',
			reloc->r_address,
			reloc->r_name == CodeSegment ? 'C' :
			reloc->r_name == DataSegment ? 'D' : 'B',
			reloc->r_offset, reloc->r_bytes, reloc->r_bigend);
	}
}

/*
 * RelocSym()
 *
 * Make a symbol-relative relocation entry at current point.
 */

void
RelocSym(SymOff symoff, int bigendian, int bytes)
{
	struct ObjReloc	*reloc;

	(reloc = Reloc(NameID(gObjNames, Name(gNames, symoff->id)),
		symoff->offset, bigendian, bytes))->r_segrel = FALSE;

	if (gDebug)
	{
		fprintf(stderr, "RELOC at %c $%04X : \"%s\" + $%X "
						"(bytes=$%X bige=%d)\n",
			gSegment == CodeSegment ? 'C' :
			gSegment == DataSegment ? 'D' : 'B',
			reloc->r_address,
			Name(gObjNames, reloc->r_name),
			reloc->r_offset,
			reloc->r_bytes, reloc->r_bigend);
	}
}

/*
 * Reloc()
 */

static	struct	ObjReloc	*
Reloc(unsigned name, int offset, int bigendian, int bytes)
{
	struct ObjReloc	*reloc = NULL;

	switch (gSegment)
	{
		case CodeSegment:
			if (gNumCodeRelocs >= MAX_RELOCATIONS)
				cerror("out of relocation entries");
			reloc = gRelocCode + gNumCodeRelocs++;
			break;
		case DataSegment:
			if (gNumDataRelocs >= MAX_RELOCATIONS)
				cerror("out of relocation entries");
			reloc = gRelocData + gNumDataRelocs++;
			break;
		case BssSegment:		/* error generated elsewhere */
			break;
		default:
			assert(0);
	}

	reloc->r_name		= name;
	reloc->r_offset		= offset;
	reloc->r_address	= gAddress[gSegment];
	reloc->r_bigend		= bigendian ? TRUE : FALSE;
	reloc->r_bytes		= bytes;

	return(reloc);
}

/*
 * RelocDumpEntries()
 *
 * Dump all relocation entries for specified segment to a file
 */

unsigned
RelocDumpEntries(FILE *fp, int segment)
{
	int				k, num = 0;
	struct ObjReloc	*relocs = NULL;

/* Point to entries */

	switch (segment)
	{
		case CodeSegment:
			relocs = gRelocCode;
			num = gNumCodeRelocs;
			break;
		case DataSegment:
			relocs = gRelocData;
			num = gNumDataRelocs;
			break;
		default:
			assert(0);
	}

/* Write them */

	for (k = 0; k < num; k++)
		if (fwrite(relocs + k, sizeof(*relocs), 1, fp) != 1)
		{
			perror("fwrite");
			cerror("can't write relocation info");
		}

/* Done */

	return(num);
}

