/** @file
 *  Reading the EM object file
 */

/* $Id$ */

#include	<stdio.h>
#include	"local.h"		/* for VERSION */
#include	"em_spec.h"
#include	"as_spec.h"		/* for as_magic */

#include	"logging.h"
#include	"nofloat.h"
#include	"global.h"
#include	"log.h"
#include	"io.h"
#include	"data.h"
#include	"proctab.h"
#include	"warn.h"
#include	"mem.h"
#include	"shadow.h"
#include	"read.h"
#include	"text.h"

#ifndef	NOFLOAT
extern double str2double();
#endif	/* NOFLOAT */

/************************************************************************
 *	Read object file contents.					*
 ************************************************************************
 *									*
 *	rd_open()	- open object file.				*
 *	rd_header()	- read object file header.			*
 *	rd_text()	- read program text.				*
 *	rd_gda()	- read global data area.			*
 *	rd_proctab()	- read procedure descriptors,			*
 *	rd_close()	- close object file.				*
 *									*
 ************************************************************************/

/* EM header Part 1 variables */

int FLAGS;

/* EM header Part 2 variables */

size NTEXT;
size NDATA;
long NPROC;
long ENTRY;
long NLINE;
size SZDATA;

PRIVATE FILE *load_fp; /* Filepointer of load file */

PRIVATE ptr rd_repeat(ptr, size, ptr);
PRIVATE ptr rd_descr(int, size, ptr);
PRIVATE int rd_byte(void);
PRIVATE long rd_int(size);

void rd_open(char *fname)
{ /* Open loadfile */
	if ((load_fp = fopen(fname, "rb")) == NULL)
	{
		fatal("Cannot open loadfile '%s'", fname);
	}
}

void rd_header(void)
{
	/* Part 1 */
	if (rd_int(2L) != as_magic)
		fatal("Bad magic number in loadfile");

	FLAGS = rd_int(2L);

	if (rd_int(2L) != 0)
		fatal("Unresolved references in loadfile");

	if (rd_int(2L) != VERSION)
		fatal("Incorrect version number in loadfile");

	/* We only allow the following wordsize/pointersize combinations: */
	/*	2/2, 2/4, 4/4						  */
	/* A fatal error will be generated if other combinations occur.   */

	wsize = rd_int(2L);
	if (!(wsize == 2 || wsize == 4))
		fatal("Bad wordsize in loadfile");

	dwsize = 2 * wsize; /* set double wordsize */
	wsizem1 = wsize - 1; /* wordsize - 1 used often */

	psize = rd_int(2L);
	if (!(psize == 2 || psize == 4) || psize < wsize)
		fatal("Bad pointersize in loadfile");
	if (2 * psize > FRALimit)
		fatal("FRA maximum size too small");

	rd_int(2L); /* Entry 7 is unused */
	rd_int(2L); /* Entry 8 is unused */

	/* Part 2 */
	NTEXT = rd_int(psize);
	NDATA = rd_int(psize);
	NPROC = rd_int(psize);
	ENTRY = rd_int(psize);
	if (ENTRY < 0 || ENTRY >= NPROC)
		fatal("Bad entry point");
	NLINE = rd_int(psize);
	if (NLINE == 0)
	{
		warning(WNLINEZR);
		NLINE = I_MAXS4;
	}
	SZDATA = rd_int(psize);

	rd_int(psize); /* entry 7 is unused */
	rd_int(psize); /* entry 8 is unused */
}

void rd_text(void)
{
	fread(text, 1, (int) DB, load_fp);
}

void rd_gda(void)
{
	register int type, prev_type;
	register ptr pos, prev_pos; /* prev_pos invalid if prev_type==0 */
	register long i;

	type = prev_type = 0;
	pos = prev_pos = i2p(0);
	for (i = 1; i <= NDATA; i++)
	{
		type = btol(rd_byte());
		LOG((" r6 rd_gda(), i = %ld, pos = %u", i, pos));
		if (type == 0)
		{
			/* repetition descriptor */
			register size count = rd_int(psize);

			LOG((" r6 rd_gda(), case 0: count = %lu", count));
			if (prev_type == 0)
			{
				fatal("Type 0 initialisation on type 0");
			}
			pos = rd_repeat(pos, count, prev_pos);
			prev_type = 0;
		}
		else
		{
			/* filling descriptor */
			register size count = btol(rd_byte());

			LOG((" r6 rd_gda(), case %d: count = %lu", type, count));
			prev_pos = pos;
			pos = rd_descr(type, count, prev_pos);
			prev_type = type;
		}
	}

	/* now protect the LIN and FIL area */
	dt_prot(i2p(0), (long)LINSIZE);
	dt_prot(i2p(4), psize);
}

void rd_proctab(void)
{
	register long p;

	init_proctab();
	for (p = 0; p < NPROC; p++)
	{
		register long nloc = rd_int(psize);
		register ptr ep = i2p(rd_int(psize));

		add_proc(nloc, ep);
	}
	end_init_proctab();
}

void rd_close(void)
{
	fclose(load_fp);
	load_fp = 0;
}

/************************************************************************
 *	Read functions for several types.				*
 ************************************************************************
 *									*
 *	rd_repeat()	- repeat the previous initialisation		*
 *	rd_descr()	- read a descriptor				*
 *	rd_byte()	- read one byte, return a int.			*
 *	rd_int(n)	- read n byte integer, return a long.		*
 *									*
 ************************************************************************/

/************************************************************************
 *		Reading a floating point number				*
 *									*
 *	A double is 8 bytes, so it can contain 4- and 8-byte (EM)	*
 *	floating point numbers. That's why a 4-byte floating point	*
 *	number is also stored in a double.				*
 ************************************************************************/

PRIVATE ptr rd_repeat(ptr pos, size count, ptr prev_pos)
{
	register size diff = pos - prev_pos;
	register size j;

	for (j = 0; j < count; j++)
	{
		register long i;

		for (i = 0; i < diff; i++)
		{
			data_loc(pos) = data_loc(pos - diff);
#ifdef	LOGGING
			/* copy shadow byte, including protection bit */
			dt_sh(pos) = dt_sh(pos - diff);
#endif	/* LOGGING */
			pos++;
		}
	}
	return pos;
}

PRIVATE ptr rd_descr(int type, size count, ptr pos)
{
	register size j;
	char fl_rep[128]; /* fp number representation */
	register int fl_cnt;

	switch (type)
	{
	case 1: /* m uninitialized words */
		j = count;
		while (j--)
		{
			dt_stw(pos, 0L);
			pos += wsize;
		}
		break;
	case 2: /* m initialized bytes */
		j = count;
		while (j--)
		{
			dt_stn(pos++, btol(rd_byte()), 1L);
		}
		break;
	case 3: /* m initialized wordsize integers */
		for (j = 0; j < count; j++)
		{
			dt_stw(pos, rd_int(wsize));
			pos += wsize;
		}
		break;
	case 4: /* m initialized data pointers */
		for (j = 0; j < count; j++)
		{
			dt_stdp(pos, i2p(rd_int(psize)));
			pos += psize;
		}
		break;
	case 5: /* m initialized instruction pointers */
		for (j = 0; j < count; j++)
		{
			dt_stip(pos, i2p(rd_int(psize)));
			pos += psize;
		}
		break;
	case 6: /* initialized integer of size m */
	case 7: /* initialized unsigned int of size m */
		if ((j = count) != 1 && j != 2 && j != 4)
			fatal("Bad integersize during initialisation");
		dt_stn(pos, rd_int(j), j);
		pos += j;
		break;
	case 8: /* initialized float of size m */
		if ((j = count) != 4 && j != 8)
			fatal("Bad floatsize during initialisation");
		/* get fp representation */
		fl_cnt = 0;
		while ( (fl_rep[fl_cnt] = rd_byte()) )
		{
			fl_cnt++;
			if (fl_cnt >= sizeof(fl_rep))
			{
				fatal("Initialized float longer than %d chars", sizeof(fl_rep));
			}
		}
#ifndef	NOFLOAT
		/* store the float */
		dt_stf(pos, str2double(fl_rep), j);
#else	/* NOFLOAT */
		/* we cannot store the float */
		warning(WFLINIT);
#endif	/* NOFLOAT */
		pos += j;
		break;
	default:
		fatal("Unknown initializer type in global data.");
		break;
	}
	return pos;
}

PRIVATE int rd_byte(void)
{
	register int i;

	if ((i = getc(load_fp)) == EOF)
		fatal("EOF reached during initialization");
	return (i);
}

PRIVATE long rd_int(size n)
{
	register long l;
	register int i;

	l = btol(rd_byte());
	for (i = 1; i < n; i++)
	{
		l |= (btol(rd_byte()) << (i * 8));
	}
	return (l);
}

