ms_divmul.c 3.02 KB
/*
 * Copyright (C) 1996-1998 by the Board of Trustees
 *    of Leland Stanford Junior University.
 * 
 * This file is part of the SimOS distribution. 
 * See LICENSE file for terms of the license. 
 *
 */


	/*
	 *  ms_divmul  -  Code to simulate integer divide and multiply
	 *		  instructions
	 *
	 *	Jim Bennett
	 *	1993, 1994
	 */

#include "ms.h"

#ifdef LITTLE_ENDIAN

#define	UPPER(reg)	((reg)|0x01)
#define	LOWER(reg)	((reg)&(~0x01))

#else

#define	UPPER(reg)	((reg)&(~0x01))
#define	LOWER(reg)	((reg)|0x01)

#endif

#define	LOW

void opMULT (struct s_cpu_state *st, INST *ip, THREAD *th)
{
	uint	a, b, c, d, t;
	int	rs, rt, rshi, rthi;
	WorkDecls;

		/* Get hi and lo half words of each word	*/
	rs = Ireg(ip->r2);
	rt = Ireg(ip->r3);
	rshi = rs>>16;
	a = rshi;
	b = rs&0xffff;
	rthi = rt>>16;
	c = rthi;
	d = rt&0xffff;

		/* Compute hi and lo result.  Note the carry out from	*/
		/* lo_reg to hi_reg.					*/
	t = (b*c + a*d)<<16;
	Ireg(LOWER(ip->r1)) = t + b*d;
	Ireg(UPPER(ip->r1)) = b*c + a*d;
	Ireg(UPPER(ip->r1)) = Ireg(UPPER(ip->r1))>>16;
	Ireg(UPPER(ip->r1)) = Ireg(UPPER(ip->r1)) + a*c;
	if (t+b*d < t) Ireg(UPPER(ip->r1))++;

		/* Make the result unavailable until the multiply	*/
		/* latency has transpired.				*/
#if MULT_LATENCY > 1
	st->reg_excuse[(UPPER(ip->r1)>>1)] = ST_IMUL;
	Add_to_worklist (st, MULT_LATENCY, reg_writeback, (void *)(ip->r1));
#endif
}

void opMULTU (struct s_cpu_state *st, INST *ip, THREAD *th)
{
	uint	a, b, c, d, t1, t2, rs, rt;
	WorkDecls;

		/* Get hi and lo half words of each word	*/
	rs = Ireg(ip->r2);
	rt = Ireg(ip->r3);
	a = (rs>>16) & 0xffff;
	b = rs&0xffff;
	c = (rt>>16) & 0xffff;
	d = rt&0xffff;

		/* Compute hi and lo result.  Note the carry out from	*/
		/* lo_reg to hi_reg, and within hi_reg.			*/
	t1 = (b*c + a*d)<<16;
	Ireg(LOWER(ip->r1)) = t1 + b*d;
	t2 = b*c + a*d;
	Ireg(UPPER(ip->r1)) = ((t2>>16) & 0xffff) + a*c;
	if (t1+b*d < t1) Ireg(UPPER(ip->r1))++;
	if (t2 < b*c) Ireg(UPPER(ip->r1)) += 0x00010000;

		/* Make the result unavailable until the multiply	*/
		/* latency has transpired.				*/
#if MULT_LATENCY > 1
	st->reg_excuse[(UPPER(ip->r1)>>1)] = ST_IMUL;
	Add_to_worklist (st, MULT_LATENCY, reg_writeback, (void *)(ip->r1));
#endif
}

void opDIV (struct s_cpu_state *st, INST *ip, THREAD *th)
{
	int	a, b;
	WorkDecls;

	a = Ireg(ip->r2);
	b = Ireg(ip->r3);
	if (b == 0)
		{
		Ireg(LOWER(ip->r1)) = 0;
		Ireg(UPPER(ip->r1)) = 0;
		}
	else
		{
		Ireg(LOWER(ip->r1)) = a/b;
		Ireg(UPPER(ip->r1)) = a-(Ireg(LOWER(ip->r1))*b);
		}
#if DIV_LATENCY > 1
	st->reg_excuse[(UPPER(ip->r1)>>1)] = ST_IDIV;
	Add_to_worklist (st, DIV_LATENCY, reg_writeback, (void *)(ip->r1));
#endif
}

void opDIVU (struct s_cpu_state *st, INST *ip, THREAD *th)
{
	uint	a, b;
	WorkDecls;

	a = Ireg(ip->r2);
	b = Ireg(ip->r3);
	if (b == 0)
		{
		Ireg(LOWER(ip->r1)) = 0;
		Ireg(UPPER(ip->r1)) = 0;
		}
	else
		{
		Ireg(LOWER(ip->r1)) = a/b;
		Ireg(UPPER(ip->r1)) = a-(Ireg(LOWER(ip->r1))*b);
		}
#if DIV_LATENCY > 1
	st->reg_excuse[(UPPER(ip->r1)>>1)] = ST_IDIV;
	Add_to_worklist (st, DIV_LATENCY, reg_writeback, (void *)(ip->r1));
#endif
}