/*
 * dohunk.c -  Do hunk output & relocation for unhunk program.
 *             Copyright 1986 by Eric D. Black.
 *			   Permission is given to redistribute this program
 *			   and its source code subject to the
 *             restrictions given in the file "unhunk.c"
 *
 */

#include <stdio.h>
#include "convert.h"
#include "hunk.h"
#include "bin.out.h"

#ifdef DBUG
#include <local/dbug.h>
#else
#include "dbugstubs.h"
#endif


/* hunk information */
extern Hinfo **hunk;           /* -> vector of ptrs to Hinfo structs */
extern Hinfo *firsthunk;       /* -> first hunk */
extern int nhunks;             /* #hunks in input file */
extern int curhunk;            /* current hunk# */
extern int hvalid;             /* state variable (to bump curhunk) */
extern long filpos;            /* position in input file */
extern long siz[3];            /* segment sizes */
extern long maxhunk;		   /* size of biggest hunk found */

extern long *hunkbuf;          /* -> buffer for doing hunk relocation */
extern char *secname;		   /* "Text", "Data", "BSS" section name strs */


/*
 * Generate memory image file from hunk format load file and
 * information collected into Hinfo structs by 1st pass in convert()
 */
output(ifd, ofd)
int ifd, ofd;
{
	struct bin bin_hd;
	int i;
	Hinfo *hu;

	DBUG_ENTER("output");

	/*
	 * Write header, then hunks to output file in sorted order;
	 * for each hunk, read it into buffer, read relocation
	 * info & do relocation; then write buffer to output file
	 */

	bin_hd.b_magic = BMAGIC;
	bin_hd.b_text = siz[TEXT];
	bin_hd.b_data = siz[DATA];
	bin_hd.b_bss = siz[BSS];
	bin_hd.b_txorg = origin[TEXT];
	bin_hd.b_dtorg = origin[DATA];
	bin_hd.b_bsorg = origin[BSS];
	bin_hd.b_entry = entrypnt;
	for (i=0; i<8; i++)
		bin_hd.b_rsrv[i] = 0;

	if (write(ofd, &bin_hd, sizeof(bin_hd)) != sizeof(bin_hd)) {
		panic("Can't write output file\n");
	}


	DBUG_3("mem","malloc'ing %d bytes for hunkbuf", maxhunk);
    if ((hunkbuf = (long *)malloc((int)maxhunk)) == NULL)
        allerr();

	for (hu = firsthunk; hu != NULL; hu = hu->hnext)
		if (hu->htype == TEXT || hu->htype == DATA) {
			lseek(ifd, hu->hpos, 0);		/* get hunk */
			if (read(ifd, hunkbuf, (int)hu->hsize) != (int)hu->hsize)
				panic("Error reading input file.");
			DBUG_4("rel","hunk %d, %ld bytes", hu->hunkno, (int)hu->hsize);
			/*
			 * relocate hunk in buffer
			 */
			if(hu->hsize) {		/* for nonzero-size hunks */
				reloc(hunkbuf, ifd, hu);	/* relocate hunk */
				if (write(ofd, hunkbuf, (int)hu->hsize) != (int)hu->hsize)
					panic("Error writing output file.");
			} else {
				DBUG_3("rel","hunk %d zero length", hu->hunkno);
			}
		}

	DBUG_2("mem", "freeing hunkbuf");
	free(hunkbuf);

	DBUG_RETURN(0);
}


/*
 * Read relocation records from input hunk file, perform relocation
 * on hunk in buffer
 */
reloc(buffer, fd, hp)
char *buffer;
int fd;	
Hinfo *hp;
{
	long pos;
	long hunksize;
	long numoffset;
	long hunknum;
	long reloffset;
	Hinfo *h;

	DBUG_ENTER("reloc");

	pos = hp->hrel;
	if(pos==0L) {
		DBUG_2("rel", "no relocation info for this hunk");
		DBUG_RETURN(0);
	}
	hunksize = hp->hsize;

	DBUG_4("rel","reloc start filepos %ld, buffer at 0x%lx", pos, buffer);
	lseek(fd, pos, 0);			/* go to 1st relocation record this hunk */
	do {
		getlong(fd, &numoffset);	/* get # of offsets to relocate */
		DBUG_3("rel","%ld offsets", numoffset);
		if (numoffset) {
			getlong(fd, &hunknum);	/* add address of this hunk ... */
			if((int)hunknum > nhunks) {
				printf("Bad hunk # in relocation info: %d, nhunks=%d\n",
					hunknum, nhunks);
				panic("phase error");
			}
			h = hunk[hunknum];
			DBUG_4("rel","  hunk %ld (base=0x%lx)",hunknum,h->haddr);
			while(numoffset--) {
				getlong(fd, &reloffset);	/* ... to word at this offset */
				DBUG_5("rel","  offset 0x%lx (addr 0x%lx) = 0x%lx",reloffset,buffer+reloffset,*(long *)(buffer+reloffset));
				if(reloffset>hunksize-3) {
					fprintf(stderr,
					    "Unreasonable relocation offset 0x%lx in %s hunk %d",
					    reloffset, secname[h->htype], h->hunkno);
					panic("Input file possibly garbled");
				}
				/*
				 * Add base address of specified hunk to the contents of
				 * longword at specified byte offset
				 * (isn't the combination of bytes & longwords clear??)
				 */
				*(long *)(buffer+reloffset) += h->haddr;
				DBUG_3("rel","  new contents = 0x%lx", *(long *)(buffer+reloffset));
			}
		}
	} while (numoffset);

	DBUG_RETURN(0);
}

