

/*************************************************************************

                      Copyright (c) 1984 by Nick de Smith

        This software is supplied for interest and non-profit making
        purposes  only.   Under  no  circumstance  shall it be lent,
        copied or otherwise used for profit.  All  rights  regarding
        the  use  and  ownership of this software shall at all times
        remain with the author, who does not guarantee the  accuracy
        or  reliabilty  of this software and who will not accept any
        liability for its use.

        This software may not be copied or distributed  without  the
        inclusion of the above copyright notice.

        January 31st 1984

*************************************************************************/


/*************************************************************************


	Program :	CAM

	Module	:	IP1.C

	Author	:	Nick de Smith		November/December 1982

	Description :
			More input utilities for the object module dis-
			assembler. This bunch is for the code generation
			stuff.

*************************************************************************/

#define	MODULE

#include	<stdio.h>

#include	"cam.h"


/*************************************************************************
*
*
*			g e t _ i t e m
*			---------------
*
*	Get the next data item from the stack, if there is any on it
*	else from the input file. Return the flags as set. This is the
*	only function that you can call to get data from the object file.
*	it will return the next item from a text record, be it byte, word
*	or whatever. Its up to the caller to determine what to do with
*	the the item by looking at the flags.
*
*************************************************************************/
global
get_item(d_ptr)
register DATA_PTR d_ptr;
{
	return(stack_p ? off_stack(d_ptr) : get_data(d_ptr));
}


/*************************************************************************
*
*
*			g e t _ d a t a
*			---------------
*
*	Get the next data item from the input file. Return flags.
*
*************************************************************************/
local
get_data(d_ptr)
register DATA_PTR d_ptr;
{
	register int flags;
	register int byte;
	byte = FALSE;

	for (;;) {

		if (eom)
			return (A_EOM);

		if ((flags = b_current->b_flags) == R_LMOD)
			return (A_LMOD);

		if (flags == R_LDEF)
			return (A_LDEF);

		if (b_current->b_left) {

			if (flags & R_RELA && b_current->b_next == r_disp) {
				if (r_byte)
					byte = TRUE;
				r_reld(d_ptr);
			}

			if (seg_ptr->p_dot & 1 || b_current->b_left == 1 ||
			   (flags & R_RELA && (b_current->b_next + 1) == r_disp))
				byte = TRUE;

			if (byte) {

				d_ptr->d_value = next_byte(b_current);
				seg_ptr->p_dot++;
				return (d_ptr->d_flags |= A_DATA | A_VALID | A_B);

			}

			d_ptr->d_value = next_two_bytes(b_current);
			seg_ptr->p_dot += 2;
			return (d_ptr->d_flags |= A_DATA | A_VALID);

		}
		if (get_text() == R_EOM)
			eom = TRUE;
	}
}


/*************************************************************************
*
*
*			r _ r e l d
*			-----------
*
*	Evaluate a relocation directory entry for the current data
*	element.
*
*************************************************************************/
local
r_reld(d_ptr)
register DATA_PTR d_ptr;
{
	int	name[2];
	int	value;
	SYMBOL_PTR	lookup();
	char	*radmin();

	d_ptr->d_flags |= A_RELA;
	d_ptr->d_text[0] = '\0';

	switch (r_command) {

		case T_IREL:
			value = next_two_bytes(b_other);
			if (pass == 3) {
				if (value == seg_ptr->p_dot) {
					buff[0] = '.';
					buff[1] = '\0';
				}
				else
					radmin((lookup(seg_cur, value))->s_name);
				cpystr(d_ptr->d_text, buff);
			}
			else
			if (value != seg_ptr->p_dot)
				insert(seg_cur, value, name,
					S_WEAK | S_DREF | S_EXT);
			d_ptr->d_flags |= A_R;
			break;

		case T_IDREL:
			value = next_two_bytes(b_other);
			if (pass == 3)
				sprintf(d_ptr->d_text, "%o", value);
			d_ptr->d_flags |= A_R;
			break;

		case T_GREL:
		case T_GDREL:
			name[0] = next_two_bytes(b_other);
			name[1] = next_two_bytes(b_other);
			if (pass == 3)
				cpystr(d_ptr->d_text, radmin(name));
			d_ptr->d_flags |= A_G;
			break;

		case T_GAREL:
		case T_GADREL:
			name[0] = next_two_bytes(b_other);
			name[1] = next_two_bytes(b_other);
			value   = next_two_bytes(b_other);
			if (pass == 3) {
				if (value)
					sprintf(d_ptr->d_text, "%s+%o",	radmin(name), value);
				else
					cpystr(d_ptr->d_text, radmin(name));
			}
			d_ptr->d_flags |= A_G;
			break;

		case T_LDEF:
		case T_LMOD:
			bug("LDEF/LMOD when not expected");

		case T_LIMIT:
			d_ptr->d_flags |= A_LIMIT;
			break;

		case T_PREL:
		case T_PDREL:
			name[0] = next_two_bytes(b_other);
			name[1] = next_two_bytes(b_other);
			if (pass == 3)
				cpystr(d_ptr->d_text, radmin((lookup(find(name), 0))->s_name));
			else
				insert(find(name), 0, name,
					S_WEAK | S_DREF | S_EXT);
			d_ptr->d_flags |= A_R;
			break;

		case T_PAREL:
		case T_PADREL:
			name[0] = next_two_bytes(b_other);
			name[1] = next_two_bytes(b_other);
			value   = next_two_bytes(b_other);
			if (pass == 3)
				cpystr(d_ptr->d_text, radmin((lookup(find(name), value))->s_name));
			else
				insert(find(name), value, name,
					S_WEAK | S_DREF | S_EXT);
			d_ptr->d_flags |= A_R;
			break;

		case T_CREL:
			eval_crel(d_ptr);
			d_ptr->d_flags |= A_C;
			break;

		case T_RLREL:
			warn("Reslib relocation detected\n");
			value = next_two_bytes(b_other);
			if (pass == 3) {
				if (value)
					sprintf(d_ptr->d_text,
						"{task/lib base}+%o", value);
				else
					cpystr(d_ptr->d_text, "{task/lib base}");
			}
			d_ptr->d_flags |= A_R;
			break;

		default:
			bug("Strange relocation entry, type %06o", r_command);
	}
	r_next(b_other);
}


/*************************************************************************
*
*
*			e v a l _ c r e l
*			-----------------
*
*	Evaluate a complex relocation RLD entry. Note that there is a
*	nasty hack in here. Because I use struct DATA for everything, the
*	'dot' has a spare 'd_text[]' which was just going to waste. In
*	order to evaluate a CREL expression a scratch buffer is needed
*	to build strings, and wbuf can't be used as it may already contain
*	some valid argument, so I used dot.d_text[], YUK!
*
*************************************************************************/
local
eval_crel(d_ptr)
register DATA_PTR d_ptr;
{
	register int type;
	register char *temp;
	int value, seg, offset, name[2];

	SYMBOL_PTR	lookup();
	char	*radmin();

	c_sp = 0;

	while ((type = next_byte(b_other)) != C_STORE && type != C_DSTORE) {

		switch (type) {

			case C_NOP:
				break;

			case C_PLUS:
			case C_MINUS:
			case C_MUL:
			case C_DIV:
			case C_AND:
			case C_OR:
				if (pass != 3) {
					c_stack[--c_sp - 1].c_prio = comp_prio[type];
					break;
				}
				temp = cpystr(dot.d_text, c_stack[c_sp -= 2].c_text);
				temp = cpystr(temp, comp_text[type]);
				if (comp_prio[type] >= c_stack[++c_sp].c_prio)
					*temp++ = '<';
				temp = cpystr(temp, c_stack[c_sp].c_text);
				if (comp_prio[type] >= c_stack[c_sp--].c_prio)
					*temp++ = '>';
				*temp = '\0';
				cpystr(c_stack[c_sp].c_text, dot.d_text);
				c_stack[c_sp++].c_prio = comp_prio[type];
				break;

			case C_UMINUS:
			case C_UCOMP:
				if (pass != 3) {
					c_stack[c_sp - 1].c_prio = comp_prio[type];
					break;
				}
				temp = cpystr(dot.d_text, comp_text[type]);
				if (comp_prio[type] > c_stack[--c_sp].c_prio)
					*temp++ = '<';
				temp = cpystr(temp, c_stack[c_sp].c_text);
				if (comp_prio[type] > c_stack[c_sp].c_prio)
					*temp++ = '>';
				*temp = '\0';
				cpystr(c_stack[c_sp].c_text, dot.d_text);
				c_stack[c_sp++].c_prio = comp_prio[type];
				break;

			case C_FGLOB:
				name_text(b_other);
				if (pass == 3)
					cpystr(c_stack[c_sp].c_text, buff);
				c_stack[c_sp++].c_prio = 10;
				break;

			case C_FREL:
				seg    = next_byte(b_other);
				offset = next_two_bytes(b_other);
				value  = (seg == seg_cur &&
						offset == seg_ptr->p_dot);
				if (pass == 3) {
					if (value) {
						buff[0] = '.';
						buff[1] = '\0';
					} else
						radmin((lookup(seg, offset))->s_name);
					cpystr(c_stack[c_sp].c_text, buff);
				} else
					if (!value)
						insert(seg, offset, name,
							S_WEAK | S_DREF | S_EXT);
				c_stack[c_sp++].c_prio = 10;
				break;

			case C_FCONST:
				value = next_two_bytes(b_other);
				if (pass == 3)
					sprintf(c_stack[c_sp].c_text, "%o", value);
				c_stack[c_sp++].c_prio = 10;
				break;

			case C_FRB:
				warn("Complex reference to reslib/task base\n");
				if (pass == 3)
					cpystr(c_stack[c_sp].c_text, "{reslib/task base}");
				c_stack[c_sp++].c_prio = 10;
				break;

			default:
				bug("Bad complex relocation entry, %06o", type);
		}
	}

	if (c_sp != 1)
		bug("Invalid CREL RPN expression");
	if (pass == 3)
		cpystr(d_ptr->d_text, c_stack[0].c_text);
}


/*************************************************************************
*
*
*			g e t _ t e x t
*			---------------
*
*	Find the next text record.
*
*************************************************************************/
local
get_text()
{
	if (b_current->b_flags == R_LDEF)
		return (R_LDEF);

	if (b_current->b_flags == R_LMOD)
		return (R_LMOD);

	b_current->b_flags = R_JUNK;

	b_switch();

	switch (b_current->b_flags & 0377) {

		case R_EOM:
			return (R_EOM);

		case R_LDEF:
			return (R_LDEF);

		case R_LMOD:
			return (R_LMOD);

		case R_JUNK:
			return (r_junk());

		case R_TEXT:
			return (r_text());

		default:
			bug("Strange buffer type, %06o, in GET_TEXT",
				b_current->b_flags);

	}
}


/*************************************************************************
*
*
*			r _ j u n k
*			-----------
*
*	Deal with a junk current buffer.
*
*************************************************************************/
local
r_junk()
{
	register int *flags;

	flags = &(b_current->b_flags);

	do {

		read_record(b_current);

		switch (next_word(b_current)) {

			case T_TEXT:
				*flags = R_TEXT;
				return (r_text());

			case T_RELD:
				if (r_next(b_current) != R_LMOD &&
				   *flags != R_LDEF)
					bug("Unexpected relocation record");
				break;

			case T_EOM:
				*flags = R_EOM;
				break;

			default:
				*flags = R_JUNK;
				break;

		}

	} while (*flags == R_JUNK);

	return (*flags);
}


/*************************************************************************
*
*
*			r _ t e x t
*			-----------
*
*	Deal with a text record. Check the next record to see if it
*	might be a relocation record which could affect this text record.
*	Note that a relocation record containing just LDEF and LMOD type
*	entries might crop up, so r_next() checks for this.
*
*************************************************************************/
local
r_text()
{
	int load;

	if ((load = next_word(b_current)) != seg_ptr->p_dot)
		if (i_flag)
			warn("Load address mis-match, is %06o, should be %06o\n",
				load, seg_ptr->p_dot);
		else
			bug("Load address mis-match, is %06o, should be %06o",
				load, seg_ptr->p_dot);
	r_other();

	return (b_current->b_flags);
}


/*************************************************************************
*
*
*			r _ n e x t
*			-----------
*
*	Get the next relocation entry from the specified buffer and
*	change the buffer characteristics if needs be. Note that this
*	function may recurse when dealing with relocation records in
*	b_other. This was introduced to deal with people who want to
*	dis-assemble .STB files, as TKB produces funny object modules
*	for these. Try CAM on RMSRES.STB one day!
*
*************************************************************************/
global
r_next(b_ptr)
register BUFFER_PTR b_ptr;
{
	if (!(b_ptr->b_left)) {
		b_ptr->b_flags = R_JUNK;
		return (b_ptr == b_other ? r_other() : R_JUNK);
	}

	r_byte = FALSE;
	if ((r_command = next_byte(b_ptr)) & 0200) {
		r_byte = TRUE;
		r_command &= 0177;
	}
	r_disp = next_byte(b_ptr);

	if (r_command == T_LMOD)
		return (b_ptr->b_flags = R_LMOD);

	if (r_command == T_LDEF)
		return (b_ptr->b_flags = R_LDEF);

	return (b_ptr->b_flags = R_RELD);
}


/*************************************************************************
*
*
*			r _ o t h e r
*			-------------
*
*	Read a record into the 'other' buffer until we get something
*	meaningful. ie. ignore all GSD/EGSD/ISN type entries.
*
*************************************************************************/
local
r_other()
{
	register int *flags;

	flags = &(b_other->b_flags);

	b_current->b_flags &= ~R_RELA;

	do {
		read_record(b_other);

		switch (next_word(b_other)) {

			case T_TEXT:
				*flags = R_TEXT;
				break;

			case T_RELD:
				if (r_next(b_other) == R_RELD)
					b_current->b_flags |= R_RELA;
				break;

			case T_EOM:
				*flags = R_EOM;
				break;

			default:
				*flags = R_JUNK;
				break;

		}

	} while (*flags == R_JUNK);

	return (*flags);
}


/*************************************************************************
*
*
*			o n _ s t a c k
*			---------------
*
*	Stack the data item pointed to. Note that only valid data items
*	are stacked. Note also that once the item is stacked, the data
*	structure it was taken from is marked as being unused.
*	This routine also adjusts the 'pc'.
*
*************************************************************************/
global
on_stack(d_ptr)
register DATA_PTR d_ptr;
{
	register DATA_PTR s_ptr;
	register int flags;

	if (!(d_ptr->d_flags & A_VALID))
		return;

	if (stack_p >= MAX_STACK)
		bug("Stack overflow");

	s_ptr = &stack[stack_p++];

	s_ptr->d_flags = (flags = d_ptr->d_flags);
	s_ptr->d_value = d_ptr->d_value;

	seg_ptr->p_dot--;
	if (!(flags & A_B))
		seg_ptr->p_dot--;

	if (flags & A_RELA)
		cpystr(s_ptr->d_text, d_ptr->d_text);

	d_ptr->d_flags &= ~(A_VALID | A_RELA);
}


/*************************************************************************
*
*
*			o f f _ s t a c k
*			-----------------
*
*	Get the last stacked item. Return the flags. This routine also
*	adjusts the 'pc'.
*
*************************************************************************/
global
off_stack(d_ptr)
register DATA_PTR d_ptr;
{
	register DATA_PTR s_ptr;
	register int flags;

	if (stack_p <= 0)
		bug("Stack underflow");

	s_ptr = &stack[--stack_p];

	d_ptr->d_flags = (flags = s_ptr->d_flags);
	d_ptr->d_value = s_ptr->d_value;

	seg_ptr->p_dot++;
	if (!(flags & A_B))
		seg_ptr->p_dot++;

	if (flags & A_RELA)
		cpystr(d_ptr->d_text, s_ptr->d_text);

	return (flags);
}
