1494 lines · 32.0 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * this doesn't attempt to implement MIPS floating-point properties * that aren't visible in the Inferno environment. * all arithmetic is done in double precision. * the FP trap status isn't updated. * * we emulate the original MIPS FP register model: 32-bits each, * F(2n) and F(2n+1) are a double, with lower-order word first; * note that this is little-endian order, unlike the rest of the * machine, so double-word operations will need to swap the words * when transferring between FP registers and memory. * * on some machines, we can convert to an FP internal representation when * moving to FPU registers and back (to integer, for example) when moving * from them.  the MIPS is different: its conversion instructions operate * on FP registers only, and there's no way to tell if data being moved * into an FP register is integer or FP, so it must be possible to store * integers in FP registers without conversion.  Furthermore, pairs of FP * registers can be combined into a double.  So we keep the raw bits * around as the canonical representation and convert only to and from * Internal FP format when we must (i.e., before calling the common fpi * code). */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"ureg.h"#include	"../port/fpi.h"#include	<tos.h> #ifdef FPEMUDEBUG#define DBG(bits) (fpemudebug & (bits))#define intpr _intpr#define internsane _internsane#define dbgstuck _dbgstuck#else#define DBG(bits) (0)#define internsane(i, ur)	do { USED(ur); } while(0)#define intpr(i, reg, fmt, ufp)	do {} while(0)#define dbgstuck(pc, ur, ufp)	do {} while(0)#endif #define	OFR(memb) (uintptr)&((Ureg*)0)->memb	/* offset into Ureg of memb */#define	REG(ur, r) *acpureg(ur, r)			/* cpu reg in Ureg */#define	FREG(ufp, fr) (ufp)->reg[(fr) & REGMASK]	/* fp reg raw bits */ /* * instruction decoding for COP1 instructions; integer instructions * are laid out differently. */#define OP(ul)	 ((ul) >> 26)#define REGMASK MASK(5)				/* mask for a register number */#define FMT(ul)	 (((ul) >> 21) & REGMASK)	/* data type */#define REGT(ul) (((ul) >> 16) & REGMASK)	/* source2 register */#define REGS(ul) (((ul) >> 11) & REGMASK)	/* source1 register */#define REGD(ul) (((ul) >>  6) & REGMASK)	/* destination register */#define FUNC(ul) ((ul) & MASK(6)) enum {	Dbgbasic = 1<<0,	/* base debugging: ops, except'ns */	Dbgmoves = 1<<1,	/* not very exciting usually */	Dbgregs	 = 1<<2,	/* print register contents around ops */	Dbgdelay = 1<<3,	/* branch-delay-slot-related machinery */ 	/* fpimips status codes */	Failed = -1,	Advpc,				/* advance pc normally */	Leavepc,			/* don't change the pc */	Leavepcret,			/* ... and return to user mode now */	Nomatch, 	/* no-ops */	NOP	= 0x27,			/* NOR R0, R0, R0 */	MIPSNOP = 0,			/* SLL R0, R0, R0 */ 	/* fp op-codes */	COP1	= 0x11,			/* fpu op */	LWC1	= 0x31,			/* load float/long */	LDC1	= 0x35,			/* load double/vlong */	SWC1	= 0x39,			/* store float/long */	SDC1	= 0x3d,			/* store double/vlong */ 	N = 1<<31,			/* condition codes */	Z = 1<<30,	C = 1<<29,	V = 1<<28, 	/* data types (format field values) */	MFC1	= 0,			/* and func == 0 ... */	DMFC1,				/* vlong move */	CFC1,				/* ctl word move */	MTC1	= 4,	DMTC1,	CTC1,				/* ... end `and func == 0' */	BRANCH	= 8,	Ffloat	= 16,	Fdouble,	Flong	= 20,	Fvlong, 	/* fp control registers */	Fpimp	= 0,	Fpcsr	= 31,}; typedef struct FP1 FP1;typedef struct FP2 FP2;typedef struct FPcvt FPcvt;typedef struct Instr Instr; struct Instr {	/* a COP1 instruction, broken out and registers converted */	int	iw;		/* whole word */	uintptr	pc;	int	o;		/* opcode or cop1 func code */	int	fmt;		/* operand format */	int	rm;		/* first operand register */	int	rn;		/* second operand register */	int	rd;		/* destination register */ 	Internal *fm;		/* converted from FREG(ufp, rm) */	Internal *fn;	char	*dfmt;	FPsave	*ufp;		/* fp state, including fp registers */	Ureg	*ur;		/* user registers */}; struct FP2 {	char*	name;	void	(*f)(Internal*, Internal*, Internal*);}; struct FP1 {	char*	name;	void	(*f)(Internal*, Internal*);}; struct FPcvt {	char*	name;	void	(*f)(int, int, int, Ureg *, FPsave *);}; static	int	roff[32] = {	0,       OFR(r1), OFR(r2), OFR(r3),	OFR(r4), OFR(r5), OFR(r6), OFR(r7),	OFR(r8), OFR(r9), OFR(r10), OFR(r11),	OFR(r12), OFR(r13), OFR(r14), OFR(r15),	OFR(r16), OFR(r17), OFR(r18), OFR(r19),	OFR(r20), OFR(r21), OFR(r22), OFR(r23),	OFR(r24), OFR(r25), OFR(r26), OFR(r27),	OFR(r28), OFR(sp),  OFR(r30), OFR(r31),}; /* * plan 9 assumes F24 initialized to 0.0, F26 to 0.5, F28 to 1.0, F30 to 2.0. */enum {	FZERO = 24,	FHALF = 26,};static Internal fpconst[Nfpregs] = {		/* indexed by register no. */	/* s, e, l, h */[FZERO]	{0, 0x1, 0x00000000, 0x00000000},	/* 0.0 */[FHALF]	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */[28]	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */[30]	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */}; static char *fmtnames[] = {[MFC1]	"MF",[DMFC1]	"DMF",[CFC1]	"CF",[MTC1]	"MT",[DMTC1]	"DMT",[CTC1]	"CT",[BRANCH]"BR", [Ffloat]"F",[Fdouble]"D",[Flong]	"W",[Fvlong]"L",}; static char *prednames[] = {[0]	"F",[1]	"UN",[2]	"EQ",[3]	"UEQ",[4]	"OLT",[5]	"ULT",[6]	"OLE",[7]	"ULE",[8]	"SF",[9]	"NGLE",[10]	"SEQ",[11]	"NGL",[12]	"LT",[13]	"NGE",[14]	"LE",[15]	"NGT",}; int fpemudebug = 0;			/* settable via /dev/archctl */ static ulong dummyr0;static QLock watchlock;			/* lock for watch-points */ ulong	branch(Ureg*, ulong);int	isbranch(ulong *); static int	fpimips(ulong, ulong, Ureg *, FPsave *); char *fpemuprint(char *p, char *ep){#ifdef FPEMUDEBUG	return seprint(p, ep, "fpemudebug %d\n", fpemudebug);#else	USED(ep);	return p;#endif} static ulong *acpureg(Ureg *ur, int r){	r &= REGMASK;	if (r == 0 || roff[r] == 0) {		dummyr0 = 0;		return &dummyr0;	}	return (ulong *)((char*)ur + roff[r]);} ulong *reg(Ureg *ur, int r)		/* for faultmips */{	return &REG(ur, r);} static void_internsane(Internal *i, Ureg *ur){	static char buf[ERRMAX]; 	USED(i);	if (!(DBG(Dbgbasic)))		return;	if ((unsigned)i->s > 1) {		snprint(buf, sizeof buf,			"fpuemu: bogus Internal sign at pc=%#p", ur->pc);		error(buf);	}	if ((unsigned)i->e > DoubleExpMax) {		snprint(buf, sizeof buf,			"fpuemu: bogus Internal exponent at pc=%#p", ur->pc);		error(buf);	}} /* * mips binary operations (d = n operator m) */ static voidfadd(Internal *m, Internal *n, Internal *d){	(m->s == n->s? fpiadd: fpisub)(m, n, d);} static voidfsub(Internal *m, Internal *n, Internal *d){	m->s ^= 1;	(m->s == n->s? fpiadd: fpisub)(m, n, d);} /* * mips unary operations */ static voidfrnd(Internal *m, Internal *d){	short e;	Internal tmp; 	tmp = fpconst[FHALF];	(m->s? fsub: fadd)(&tmp, m, d);	if(IsWeird(d))		return;	fpiround(d);	e = (d->e - ExpBias) + 1;	if(e <= 0)		SetZero(d);	else if(e > FractBits){		if(e < 2*FractBits)			d->l &= ~((1<<(2*FractBits - e))-1);	}else{		d->l = 0;		if(e < FractBits)			d->h &= ~((1<<(FractBits-e))-1);	}} /* debugging: print internal representation of an fp reg */static void_intpr(Internal *i, int reg, int fmt, FPsave *ufp){	USED(i);	if (!(DBG(Dbgregs)))		return;	if (fmt == Fdouble && reg < 31)		iprint("\tD%02d: l %08lux h %08lux =\ts %d e %04d h %08lux l %08lux\n",			reg, FREG(ufp, reg), FREG(ufp, reg+1),			i->s, i->e, i->h, i->l);	else		iprint("\tF%02d: %08lux =\ts %d e %04d h %08lux l %08lux\n",			reg, FREG(ufp, reg),			i->s, i->e, i->h, i->l);	delay(75);} static voiddreg2dbl(Double *dp, int reg, FPsave *ufp){	reg &= ~1;	dp->l = FREG(ufp, reg);	dp->h = FREG(ufp, reg+1);} static voiddbl2dreg(int reg, Double *dp, FPsave *ufp){	reg &= ~1;	FREG(ufp, reg)   = dp->l;	FREG(ufp, reg+1) = dp->h;} static voidvreg2dbl(Double *dp, int reg, FPsave *ufp){	reg &= ~1;	dp->l = FREG(ufp, reg+1);	dp->h = FREG(ufp, reg);} static voiddbl2vreg(int reg, Double *dp, FPsave *ufp){	reg &= ~1;	FREG(ufp, reg+1) = dp->l;	FREG(ufp, reg)   = dp->h;} /* convert fmt (rm) to double (rd) */static voidfcvtd(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){	Double d;	Internal intrn; 	switch (fmt) {	case Ffloat:		fpis2i(&intrn, &FREG(ufp, rm));		internsane(&intrn, ur);		fpii2d(&d, &intrn);		break;	case Fdouble:		dreg2dbl(&d, rm, ufp);		break;	case Flong:		fpiw2i(&intrn, &FREG(ufp, rm));		internsane(&intrn, ur);		fpii2d(&d, &intrn);		break;	case Fvlong:		vreg2dbl(&d, rm, ufp);		fpiv2i(&intrn, &d);		internsane(&intrn, ur);		fpii2d(&d, &intrn);		break;	}	dbl2dreg(rd, &d, ufp);	if (fmt != Fdouble && DBG(Dbgregs))		intpr(&intrn, rm, Fdouble, ufp);} /* convert fmt (rm) to single (rd) */static voidfcvts(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){	Double d;	Internal intrn; 	switch (fmt) {	case Ffloat:		FREG(ufp, rd) = FREG(ufp, rm);		break;	case Fdouble:		dreg2dbl(&d, rm, ufp);		fpid2i(&intrn, &d);		break;	case Flong:		fpiw2i(&intrn, &FREG(ufp, rm));		break;	case Fvlong:		vreg2dbl(&d, rm, ufp);		fpiv2i(&intrn, &d);		break;	}	if (fmt != Ffloat) {		if(DBG(Dbgregs))			intpr(&intrn, rm, Ffloat, ufp);		internsane(&intrn, ur);		fpii2s(&FREG(ufp, rd), &intrn);	}} /* convert fmt (rm) to long (rd) */static voidfcvtw(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){	Double d;	Internal intrn; 	switch (fmt) {	case Ffloat:		fpis2i(&intrn, &FREG(ufp, rm));		break;	case Fdouble:		dreg2dbl(&d, rm, ufp);		fpid2i(&intrn, &d);		break;	case Flong:		FREG(ufp, rd) = FREG(ufp, rm);		break;	case Fvlong:		vreg2dbl(&d, rm, ufp);		fpiv2i(&intrn, &d);		break;	}	if (fmt != Flong) {		if(DBG(Dbgregs))			intpr(&intrn, rm, Flong, ufp);		internsane(&intrn, ur);		fpii2w((long *)&FREG(ufp, rd), &intrn);	}} /* convert fmt (rm) to vlong (rd) */static voidfcvtv(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){	Double d;	Internal intrn; 	switch (fmt) {	case Ffloat:		fpis2i(&intrn, &FREG(ufp, rm));		break;	case Fdouble:		dreg2dbl(&d, rm, ufp);		fpid2i(&intrn, &d);		break;	case Flong:		fpiw2i(&intrn, &FREG(ufp, rm));		break;	case Fvlong:		vreg2dbl(&d, rm, ufp);		dbl2vreg(rd, &d, ufp);		break;	}	if (fmt != Fvlong) {		if(DBG(Dbgregs))			intpr(&intrn, rm, Fvlong, ufp);		internsane(&intrn, ur);		fpii2v((vlong *)&FREG(ufp, rd), &intrn);	}} /* * MIPS function codes */ static	FP2	optab2[] = {	/* Fd := Fn OP Fm (binary) */[0]	{"ADDF",	fadd},	/* can ignore fmt, just use doubles */[1]	{"SUBF",	fsub},[2]	{"MULF",	fpimul},[3]	{"DIVF",	fpidiv},

Lines 1–500 of 1494.