/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 *
 * Author: Hans van Staveren
 */
#include <assert.h>
#include "param.h"
#include "types.h"
#include "tes.h"
#include "line.h"
#include "lookup.h"
#include "alloc.h"
#include "proinf.h"
#include <em_spec.h>
#include <em_pseu.h>
#include <em_mnem.h>
#include <em_mes.h>
#include "ext.h"
#include "getline.h"
#include "reg.h"

#define local(x)	((((x)->s_flags&SYMKNOWN) == 0 && \
			  ((x)->s_flags &= ~ SYMGLOBAL)),\
			 (x)->s_flags |= SYMSEEN)
#define global(x)	((((x)->s_flags&SYMKNOWN) == 0 && \
			  ((x)->s_flags |= SYMGLOBAL)), \
			 (x)->s_flags |= SYMSEEN)

#define DTYPHOL	1
#define DTYPBSS 2
#define DTYPCON 3
#define DTYPROM 4
byte curdtyp;
bool goodrom;
short curfrag = 3; /* see also peephole.c */
offset rombuf[MAXROM];
int rc;

void backward(void)
{
	register line_p lnp;
	line_p next;
	register arg_p ap;
	line_p i, p;
	int n;
	register sym_p sp;

	i = p = (line_p) 0;
	curdtyp = 0;
	for (lnp = curpro.lastline; lnp != (line_p) 0; lnp = next)
	{
		next = lnp->l_next;
		switch (lnp->l_optyp)
		{
			case OPSYMBOL:
				global(lnp->l_a.la_sp);
				break;
			case OPSVAL:
				global(lnp->l_a.la_sval.lasv_sp);
				break;
			case OPLVAL:
				global(lnp->l_a.la_lval.lalv_sp);
				break;
			case OPLIST:
				ap = lnp->l_a.la_arg;
				while (ap != (arg_p) 0)
				{
					switch (ap->a_typ)
					{
						case ARGSYM:
							global(ap->a_a.a_sp);
							break;
						case ARGVAL:
							global(ap->a_a.a_val.av_sp);
					}
					ap = ap->a_next;
				}
				break;
		}

		/*
		 * references to symbols are processed now.
		 * for plain instructions nothing else is needed
		 */

		switch (lnp->l_instr & BMASK)
		{
			/*
			 * count all local occurences for register counts;
			 * op_lal is omitted and not by accident.
			 */
			case op_del:
			case op_inl:
			case op_ldl:
			case op_lil:
			case op_lol:
			case op_sdl:
			case op_sil:
			case op_stl:
			case op_zrl:
				switch (lnp->l_optyp)
				{
					case OPNO:
					case OPNUMLAB:
					case OPSYMBOL:
					case OPSVAL:
					case OPLVAL:
					case OPLIST:
						break;
					case OPOFFSET:
						incregusage(lnp->l_a.la_offset);
						break;
					case OPSHORT:
						incregusage((offset) lnp->l_a.la_short);
						break;
					default:
						incregusage((offset) (lnp->l_optyp & BMASK) - Z_OPMINI);
						break;
				}
				/* fall through !! */
			default:
				assert((lnp->l_instr&BMASK)<=op_last);
				lnp->l_next = i;
				i = lnp;
				continue;
			case ps_sym:
				sp = lnp->l_a.la_sp;
				local(sp);
				if (curdtyp == DTYPROM && goodrom)
				{
					sp->s_rom = newrom();
					for (n = 0; n < rc; n++)
						sp->s_rom[n] = rombuf[n];
				}
				sp->s_frag = curfrag;
				break;
			case ps_hol:
				curdtyp = DTYPHOL;
				curfrag++;
				break;
			case ps_bss:
				curdtyp = DTYPBSS;
				curfrag++;
				break;
			case ps_con:
				if (curdtyp != DTYPCON)
				{
					curdtyp = DTYPCON;
					curfrag++;
				}
				break;
			case ps_rom:
				if (curdtyp != DTYPROM)
				{
					curdtyp = DTYPROM;
					curfrag++;
				}
				ap = lnp->l_a.la_arg;
				rc = 0;
				while (ap != (arg_p) 0 && rc < MAXROM)
				{
					if (ap->a_typ == ARGOFF)
					{
						rombuf[rc++] = ap->a_a.a_offset;
						ap = ap->a_next;
					}
					else
						ap = (arg_p) 0;
				}
				goodrom = (rc >= 2);
				break;
			case ps_mes:
				if (prodepth != 0
						&& ((int) aoff(lnp->l_a.la_arg, 0) == ms_std
								|| (int) aoff(lnp->l_a.la_arg, 0) == ms_stb
								|| (int) aoff(lnp->l_a.la_arg, 0) == ms_ego))
				{
					lnp->l_next = i;
					i = lnp;
					continue;
				}
				break;
			case ps_inp:
			case ps_ina:
				local(lnp->l_a.la_sp);
			case ps_exp:
			case ps_exa:
			case ps_exc:
				oldline(lnp);
				continue;
		}
		lnp->l_next = p;
		p = lnp;
	}
	if (prodepth != 0)
		local(curpro.symbol);
	instrs = i;
	pseudos = p;
	curpro.lastline = (line_p) 0;
}