/** @file
 * Sources of the "UNSIGNED ARITHMETIC" group instructions
 */

/* $Id$ */

#include	"em_abs.h"
#include	"logging.h"
#include	"global.h"
#include	"log.h"
#include	"mem.h"
#include	"trap.h"
#include	"warn.h"
#include	"text.h"
#include	"fra.h"
#include	"switch.h"

/************************************************************************
 *	No checking is performed, except for division by zero.		*
 *	The operands popped from the stack are put in unsigned		*
 *	longs. Now the required operation can be performed		*
 *	immediately. Whether the wordsize is two or four bytes		*
 *	doesn't matter. Alas, arithmetic is performed modulo		*
 *	the highest unsigned number for the given size plus 1.		*
 ************************************************************************/

#ifdef	LOGGING
extern int must_test;
#endif	/* LOGGING */

#define	adu(w1,w2)	(unsigned long)(w1 + w2)
#define	sbu(w1,w2)	(unsigned long)(w1 - w2)
#define	mlu(w1,w2)	(unsigned long)(w1 * w2)

PRIVATE unsigned long dvu(), rmu(), slu(), sru();

/** ADU w: Addition */
void DoADU(register size l)
{
	register unsigned long t = upop(arg_wi(l));

	LOG(("@U6 DoADU(%ld)", l));
	spoilFRA();
	npush((long) adu(upop(l), t), l);
}

/** SBU w: Subtraction */
void DoSBU(register size l)
{
	register unsigned long t = upop(arg_wi(l));

	LOG(("@U6 DoSBU(%ld)", l));
	spoilFRA();
	npush((long) sbu(upop(l), t), l);
}

/** MLU w: Multiplication */
void DoMLU(register size l)
{
	register unsigned long t = upop(arg_wi(l));

	LOG(("@U6 DoMLU(%ld)", l));
	spoilFRA();
	npush((long) mlu(upop(l), t), l);
}

/** DVU w: Division */
void DoDVU(register size l)
{
	register unsigned long t = upop(arg_wi(l));

	LOG(("@U6 DoDVU(%ld)", l));
	spoilFRA();
	npush((long) dvu(upop(l), t), l);
}

/** RMU w: Remainder */
void DoRMU(register size l)
{
	register unsigned long t = upop(arg_wi(l));

	LOG(("@U6 DoRMU(%ld)", l));
	spoilFRA();
	npush((long) rmu(upop(l), t), l);
}

/** SLU w: Shift left */
void DoSLU(register size l)
{
	register unsigned long t = uwpop();

	LOG(("@U6 DoSLU(%ld)", l));
	spoilFRA();
	l = arg_wi(l);
	npush((long) slu(upop(l), t, l), l);
}

/** SRU w: Shift right */
void DoSRU(register size l)
{
	register unsigned long t = uwpop();

	LOG(("@U6 DoSRU(%ld)", l));
	spoilFRA();
	l = arg_wi(l);
	npush((long) sru(upop(l), t, l), l);
}

PRIVATE unsigned long dvu(
	unsigned long w1,
	unsigned long w2)
{
	if (w2 == 0) {
		if (!(IgnMask&BIT(EIDIVZ))) {
			trap(EIDIVZ);
		}
		else	return (0L);
	}
	return (w1 / w2);
}

PRIVATE unsigned long rmu(
	unsigned long w1,
	unsigned long w2)
{
	if (w2 == 0) {
		if (!(IgnMask&BIT(EIDIVZ))) {
			trap(EIDIVZ);
		}
		else	return (0L);
	}
	return (w1 % w2);
}

/*ARGSUSED*/
PRIVATE unsigned long slu(
		unsigned long w1,
		unsigned long w2,
		size nbytes)
{
		/* w1 << w2 */
#ifdef	LOGGING
	if (must_test) {
		/* check shift distance */
		if (w2 >= nbytes*8)	{
			warning(WSHLARGE);
			w2 = nbytes*8 - 1;
		}
	}
#endif	/* LOGGING */

	/* calculate result */
	return (w1 << w2);
}

/*ARGSUSED*/
PRIVATE unsigned long sru(
	unsigned long w1,
	unsigned long w2,
	size nbytes)
{
	/* w1 >> w2 */
#ifdef	LOGGING
	if (must_test) {
		/* check shift distance */
		if (w2 >= nbytes*8)	{
			warning(WSHLARGE);
			w2 = nbytes*8 - 1;
		}
	}
#endif	/* LOGGING */

	/* calculate result */
	return (w1 >> w2);
}