~kris/9p

9hist

900a42a41670dba9578c2c21af2bebefd339bdad — David du Colombier 25 years ago 12c6d77
Plan 9 from Bell Labs 2000-10-26
10 files changed, 1037 insertions(+), 14 deletions(-)

M bitsy/dat.h
M bitsy/fns.h
A bitsy/fpi.c
A bitsy/fpi.h
A bitsy/fpiarm.c
A bitsy/fpimem.c
M bitsy/main.c
M bitsy/screen.c
M bitsy/trap.c
M port/proc.c
M bitsy/dat.h => bitsy/dat.h +8 -7
@@ 1,4 1,6 @@
typedef struct Conf	Conf;
typedef struct FPU	FPU;
typedef struct FPenv	FPenv;
typedef struct FPsave	FPsave;
typedef struct Label	Label;
typedef struct Lock	Lock;


@@ 34,20 36,19 @@ struct Label
};

/*
 *  no floating point, hence nothing to save
 */

/*
 * FPsave.status
 */
enum
{
	FPinit,
	FPinactive,
	FPINIT,
	FPACTIVE,
	FPINACTIVE,
};
struct	FPsave
{
	int	dummy;
	ulong	status;
	ulong	control;
	ulong	regs[8][3];	/* emulated fp */	
};

struct Conf

M bitsy/fns.h => bitsy/fns.h +1 -0
@@ 12,6 12,7 @@ void	clockinit(void);
void	delay(int);
void	evenaddr(ulong);
void	flushmmu(void);
int		fpiarm(Ureg *ur);
char*	getconf(char*);
ulong	getfar(void);
ulong	getfsr(void);

A bitsy/fpi.c => bitsy/fpi.c +300 -0
@@ 0,0 1,300 @@
/*
 * Floating Point Interpreter.
 * shamelessly stolen from an original by ark.
 */
#include "fpi.h"

void
fpiround(Internal *i)
{
	unsigned long guard;

	guard = i->l & GuardMask;
	i->l &= ~GuardMask;
	if(guard > (LsBit>>1) || (guard == (LsBit>>1) && (i->l & LsBit))){
		i->l += LsBit;
		if(i->l & CarryBit){
			i->l &= ~CarryBit;
			i->h++;
			if(i->h & CarryBit){
				if (i->h & 0x01)
					i->l |= CarryBit;
				i->l >>= 1;
				i->h >>= 1;
				i->e++;
			}
		}
	}
}

static void
matchexponents(Internal *x, Internal *y)
{
	int count;

	count = y->e - x->e;
	x->e = y->e;
	if(count >= 2*FractBits){
		x->l = x->l || x->h;
		x->h = 0;
		return;
	}
	if(count >= FractBits){
		count -= FractBits;
		x->l = x->h|(x->l != 0);
		x->h = 0;
	}
	while(count > 0){
		count--;
		if(x->h & 0x01)
			x->l |= CarryBit;
		if(x->l & 0x01)
			x->l |= 2;
		x->l >>= 1;
		x->h >>= 1;
	}
}

static void
shift(Internal *i)
{
	i->e--;
	i->h <<= 1;
	i->l <<= 1;
	if(i->l & CarryBit){
		i->l &= ~CarryBit;
		i->h |= 0x01;
	}
}

static void
normalise(Internal *i)
{
	while((i->h & HiddenBit) == 0)
		shift(i);
}

static void
renormalise(Internal *i)
{
	if(i->e < -2 * FractBits)
		i->e = -2 * FractBits;
	while(i->e < 1){
		i->e++;
		if(i->h & 0x01)
			i->l |= CarryBit;
		i->h >>= 1;
		i->l = (i->l>>1)|(i->l & 0x01);
	}
	if(i->e >= ExpInfinity)
		SetInfinity(i);
}

void
fpinormalise(Internal *x)
{
	if(!IsWeird(x) && !IsZero(x))
		normalise(x);
}

void
fpiadd(Internal *x, Internal *y, Internal *i)
{
	Internal *t;

	i->s = x->s;
	if(IsWeird(x) || IsWeird(y)){
		if(IsNaN(x) || IsNaN(y))
			SetQNaN(i);
		else
			SetInfinity(i);
		return;
	}
	if(x->e > y->e){
		t = x;
		x = y;
		y = t;
	}
	matchexponents(x, y);
	i->e = x->e;
	i->h = x->h + y->h;
	i->l = x->l + y->l;
	if(i->l & CarryBit){
		i->h++;
		i->l &= ~CarryBit;
	}
	if(i->h & (HiddenBit<<1)){
		if(i->h & 0x01)
			i->l |= CarryBit;
		i->l = (i->l>>1)|(i->l & 0x01);
		i->h >>= 1;
		i->e++;
	}
	if(IsWeird(i))
		SetInfinity(i);
}

void
fpisub(Internal *x, Internal *y, Internal *i)
{
	Internal *t;

	if(y->e < x->e
	   || (y->e == x->e && (y->h < x->h || (y->h == x->h && y->l < x->l)))){
		t = x;
		x = y;
		y = t;
	}
	i->s = y->s;
	if(IsNaN(y)){
		SetQNaN(i);
		return;
	}
	if(IsInfinity(y)){
		if(IsInfinity(x))
			SetQNaN(i);
		else
			SetInfinity(i);
		return;
	}
	matchexponents(x, y);
	i->e = y->e;
	i->h = y->h - x->h;
	i->l = y->l - x->l;
	if(i->l < 0){
		i->l += CarryBit;
		i->h--;
	}
	if(i->h == 0 && i->l == 0)
		SetZero(i);
	else while(i->e > 1 && (i->h & HiddenBit) == 0)
		shift(i);
}

#define	CHUNK		(FractBits/2)
#define	CMASK		((1<<CHUNK)-1)
#define	HI(x)		((short)((x)>>CHUNK) & CMASK)
#define	LO(x)		((short)(x) & CMASK)
#define	SPILL(x)	((x)>>CHUNK)
#define	M(x, y)		((long)a[x]*(long)b[y])
#define	C(h, l)		(((long)((h) & CMASK)<<CHUNK)|((l) & CMASK))

void
fpimul(Internal *x, Internal *y, Internal *i)
{
	long a[4], b[4], c[7], f[4];

	i->s = x->s^y->s;
	if(IsWeird(x) || IsWeird(y)){
		if(IsNaN(x) || IsNaN(y) || IsZero(x) || IsZero(y))
			SetQNaN(i);
		else
			SetInfinity(i);
		return;
	}
	else if(IsZero(x) || IsZero(y)){
		SetZero(i);
		return;
	}
	normalise(x);
	normalise(y);
	i->e = x->e + y->e - (ExpBias - 1);

	a[0] = HI(x->h); b[0] = HI(y->h);
	a[1] = LO(x->h); b[1] = LO(y->h);
	a[2] = HI(x->l); b[2] = HI(y->l);
	a[3] = LO(x->l); b[3] = LO(y->l);

	c[6] =                               M(3, 3);
	c[5] =                     M(2, 3) + M(3, 2) + SPILL(c[6]);
	c[4] =           M(1, 3) + M(2, 2) + M(3, 1) + SPILL(c[5]);
	c[3] = M(0, 3) + M(1, 2) + M(2, 1) + M(3, 0) + SPILL(c[4]);
	c[2] = M(0, 2) + M(1, 1) + M(2, 0)           + SPILL(c[3]);
	c[1] = M(0, 1) + M(1, 0)                     + SPILL(c[2]);
	c[0] = M(0, 0)                               + SPILL(c[1]);

	f[0] = c[0];
	f[1] = C(c[1], c[2]);
	f[2] = C(c[3], c[4]);
	f[3] = C(c[5], c[6]);

	if((f[0] & HiddenBit) == 0){
		f[0] <<= 1;
		f[1] <<= 1;
		f[2] <<= 1;
		f[3] <<= 1;
		if(f[1] & CarryBit){
			f[0] |= 1;
			f[1] &= ~CarryBit;
		}
		if(f[2] & CarryBit){
			f[1] |= 1;
			f[2] &= ~CarryBit;
		}
		if(f[3] & CarryBit){
			f[2] |= 1;
			f[3] &= ~CarryBit;
		}
		i->e--;
	}
	i->h = f[0];
	i->l = f[1];
	if(f[2] || f[3])
		i->l |= 1;
	renormalise(i);
}

void
fpidiv(Internal *x, Internal *y, Internal *i)
{
	i->s = x->s^y->s;
	if(IsNaN(x) || IsNaN(y)
	   || (IsInfinity(x) && IsInfinity(y)) || (IsZero(x) && IsZero(y))){
		SetQNaN(i);
		return;
	}
	else if(IsZero(x) || IsInfinity(y)){
		SetInfinity(i);
		return;
	}
	else if(IsInfinity(x) || IsZero(y)){
		SetZero(i);
		return;
	}
	normalise(x);
	normalise(y);
	i->h = 0;
	i->l = 0;
	i->e = y->e - x->e + (ExpBias + 2*FractBits - 1);
	do{
		if(y->h > x->h || (y->h == x->h && y->l >= x->l)){
			i->l |= 0x01;
			y->h -= x->h;
			y->l -= x->l;
			if(y->l < 0){
				y->l += CarryBit;
				y->h--;
			}
		}
		shift(y);
		shift(i);
	}while ((i->h & HiddenBit) == 0);
	if(y->h || y->l)
		i->l |= 0x01;
	renormalise(i);
}

int
fpicmp(Internal *x, Internal *y)
{
	if(IsNaN(x) && IsNaN(y))
		return 0;
	if(IsInfinity(x) && IsInfinity(y))
		return y->s - x->s;
	if(x->e == y->e && x->h == y->h && x->l == y->l)
		return y->s - x->s;
	if(x->e < y->e
	   || (x->e == y->e && (x->h < y->h || (x->h == y->h && x->l < y->l))))
		return y->s ? 1: -1;
	return x->s ? -1: 1;
}

A bitsy/fpi.h => bitsy/fpi.h +61 -0
@@ 0,0 1,61 @@
typedef long Word;
typedef unsigned long Single;
typedef struct {
	unsigned long h;
	unsigned long l;
} Double;

enum {
	FractBits	= 28,
	CarryBit	= 0x10000000,
	HiddenBit	= 0x08000000,
	MsBit		= HiddenBit,
	NGuardBits	= 3,
	GuardMask	= 0x07,
	LsBit		= (1<<NGuardBits),

	SingleExpBias	= 127,
	SingleExpMax	= 255,
	DoubleExpBias	= 1023,
	DoubleExpMax	= 2047,

	ExpBias		= DoubleExpBias,
	ExpInfinity	= DoubleExpMax,
};

typedef struct {
	unsigned char s;
	short e;
	long l;				/* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
	long h;				/* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
} Internal;

#define IsWeird(n)	((n)->e >= ExpInfinity)
#define	IsInfinity(n)	(IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
#define	SetInfinity(n)	((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
#define IsNaN(n)	(IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
#define	SetQNaN(n)	((n)->s = 0, (n)->e = ExpInfinity, 		\
			 (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
#define IsZero(n)	((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
#define SetZero(n)	((n)->e = 1, (n)->h = 0, (n)->l = 0)

/*
 * fpi.c
 */
extern void fpiround(Internal *);
extern void fpiadd(Internal *, Internal *, Internal *);
extern void fpisub(Internal *, Internal *, Internal *);
extern void fpimul(Internal *, Internal *, Internal *);
extern void fpidiv(Internal *, Internal *, Internal *);
extern int fpicmp(Internal *, Internal *);
extern void fpinormalise(Internal*);

/*
 * fpimem.c
 */
extern void fpis2i(Internal *, void *);
extern void fpid2i(Internal *, void *);
extern void fpiw2i(Internal *, void *);
extern void fpii2s(void *, Internal *);
extern void fpii2d(void *, Internal *);
extern void fpii2w(Word *, Internal *);

A bitsy/fpiarm.c => bitsy/fpiarm.c +488 -0
@@ 0,0 1,488 @@
/*
 * this doesn't attempt to implement ARM 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.
 */
#include "u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"

#include	"ureg.h"

#include "fpi.h"

#define	R13OK
/* undef this if correct kernel r13 isn't in Ureg;
 * check calculation in fpiarm below
 */


#define	REG(x) (*(long*)(((char*)ur)+roff[(x)]))
#define	FPENV	(*ufp)
#define	FR(x) (*(Internal*)ufp->regs[(x)&7])

/* BUG: check fetch (not worthwhile in Inferno) */
#define	getubyte(a) (*(uchar*)(a))
#define	getuword(a) (*(ushort*)(a))
#define	getulong(a) (*(ulong*)(a))

typedef struct FP2 FP2;
typedef struct FP1 FP1;

struct FP2 {
	char*	name;
	void	(*f)(Internal, Internal, Internal*);
};

struct FP1 {
	char*	name;
	void	(*f)(Internal*, Internal*);
};

enum {
	N = 1<<31,
	Z = 1<<30,
	C = 1<<29,
	V = 1<<28,
	REGPC = 15,
};

int	fpemudebug = 0;

#undef OFR
#define	OFR(X)	((ulong)&((Ureg*)0)->X)

static	int	roff[] = {
	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
#ifdef R13OK
	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
#else
	OFR(r12), OFR(type), OFR(r14), OFR(pc),
#endif
};

static Internal fpconst[8] = {	/* indexed by op&7 */
	/* s, e, l, h */
	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
};

/*
 * arm binary operations
 */

static void
fadd(Internal m, Internal n, Internal *d)
{
	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
}

static void
fsub(Internal m, Internal n, Internal *d)
{
	m.s ^= 1;
	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
}

static void
fsubr(Internal m, Internal n, Internal *d)
{
	n.s ^= 1;
	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
}

static void
fmul(Internal m, Internal n, Internal *d)
{
	fpimul(&m, &n, d);
}

static void
fdiv(Internal m, Internal n, Internal *d)
{
	fpidiv(&m, &n, d);
}

static void
fdivr(Internal m, Internal n, Internal *d)
{
	fpidiv(&n, &m, d);
}

/*
 * arm unary operations
 */

static void
fmov(Internal *m, Internal *d)
{
	*d = *m;
}

static void
fmovn(Internal *m, Internal *d)
{
	*d = *m;
	d->s ^= 1;
}

static void
fabsf(Internal *m, Internal *d)
{
	*d = *m;
	d->s = 0;
}

static void
frnd(Internal *m, Internal *d)
{
	short e;

	(m->s? fsub: fadd)(fpconst[6], *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);
	}
}

static	FP1	optab1[16] = {	/* Fd := OP Fm */
[0]	{"MOVF",	fmov},
[1]	{"NEGF",	fmovn},
[2]	{"ABSF",	fabsf},
[3]	{"RNDF",	frnd},
[4]	{"SQTF",	/*fsqt*/0},
/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
/* URD and NRM aren't implemented */
};

static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
[0]	{"ADDF",	fadd},
[1]	{"MULF",	fmul},
[2]	{"SUBF",	fsub},
[3]	{"RSUBF",	fsubr},
[4]	{"DIVF",	fdiv},
[5]	{"RDIVF",	fdivr},
/* POW, RPW deprecated */
[8]	{"REMF",	/*frem*/0},
[9]	{"FMF",	fmul},	/* fast multiply */
[10]	{"FDV",	fdiv},	/* fast divide */
[11]	{"FRD",	fdivr},	/* fast reverse divide */
/* POL deprecated */
};

static ulong
fcmp(Internal *n, Internal *m)
{
	int i;

	if(IsWeird(m) || IsWeird(n)){
		/* BUG: should trap if not masked */
		return V|C;
	}
	i = fpicmp(n, m);
	if(i > 0)
		return C;
	else if(i == 0)
		return C|Z;
	else
		return N;
}

static void
fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
{
	void *mem;

	mem = (void*)ea;
	(*f)(&FR(d), mem);
	if(fpemudebug)
		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
}

static void
fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
{
	Internal tmp;
	void *mem;

	mem = (void*)ea;
	tmp = FR(s);
	if(fpemudebug)
		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
	(*f)(mem, &tmp);
}

static int
condok(int cc, int c)
{
	switch(c){
	case 0:	/* Z set */
		return cc&Z;
	case 1:	/* Z clear */
		return (cc&Z) == 0;
	case 2:	/* C set */
		return cc&C;
	case 3:	/* C clear */
		return (cc&C) == 0;
	case 4:	/* N set */
		return cc&N;
	case 5:	/* N clear */
		return (cc&N) == 0;
	case 6:	/* V set */
		return cc&V;
	case 7:	/* V clear */
		return (cc&V) == 0;
	case 8:	/* C set and Z clear */
		return cc&C && (cc&Z) == 0;
	case 9:	/* C clear or Z set */
		return (cc&C) == 0 || cc&Z;
	case 10:	/* N set and V set, or N clear and V clear */
		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
	case 11:	/* N set and V clear, or N clear and V set */
		return (cc&(N|V))==N || (cc&(N|V))==V;
	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
	case 13:	/* Z set, or N set and V clear or N clear and V set */
		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
	case 14:	/* always */
		return 1;
	case 15:	/* never (reserved) */
		return 0;
	}
	return 0;	/* not reached */
}

static void
unimp(ulong pc, ulong op)
{
	char buf[60];

	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
	if(fpemudebug)
		print("FPE: %s\n", buf);
	error(buf);
	/* no return */
}

static void
fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
{
	int rn, rd, tag, o;
	long off;
	ulong ea;
	Internal tmp, *fm, *fn;

	/* note: would update fault status here if we noted numeric exceptions */

	/*
	 * LDF, STF; 10.1.1
	 */
	if(((op>>25)&7) == 6){
		if(op & (1<<22))
			unimp(pc, op);	/* packed or extended */
		rn = (op>>16)&0xF;
		off = (op&0xFF)<<2;
		if((op & (1<<23)) == 0)
			off = -off;
		ea = REG(rn);
		if(rn == REGPC)
			ea += 8;
		if(op & (1<<24))
			ea += off;
		rd = (op>>12)&7;
		if(op & (1<<20)){
			if(op & (1<<15))
				fld(fpid2i, rd, ea, 8, ufp);
			else
				fld(fpis2i, rd, ea, 4, ufp);
		}else{
			if(op & (1<<15))
				fst(fpii2d, ea, rd, 8, ufp);
			else
				fst(fpii2s, ea, rd, 4, ufp);
		}
		if((op & (1<<24)) == 0)
			ea += off;
		if(op & (1<<21))
			REG(rn) = ea;
		return;
	}

	/*
	 * CPRT/transfer, 10.3
	 */
	if(op & (1<<4)){
		rd = (op>>12) & 0xF;

		/*
		 * compare, 10.3.1
		 */
		if(rd == 15 && op & (1<<20)){
			rn = (op>>16)&7;
			fn = &FR(rn);
			if(op & (1<<3)){
				fm = &fpconst[op&7];
				tag = 'C';
			}else{
				fm = &FR(op&7);
				tag = 'F';
			}
			switch((op>>21)&7){
			default:
				unimp(pc, op);
			case 4:	/* CMF: Fn :: Fm */
			case 6:	/* CMFE: Fn :: Fm (with exception) */
				ur->psr &= ~(N|C|Z|V);
				ur->psr |= fcmp(fn, fm);
				break;
			case 5:	/* CNF: Fn :: -Fm */
			case 7:	/* CNFE: Fn :: -Fm (with exception) */
				tmp = *fm;
				tmp.s ^= 1;
				ur->psr &= ~(N|C|Z|V);
				ur->psr |= fcmp(fn, &tmp);
				break;
			}
			if(fpemudebug)
				print("CMPF	%c%d,F%ld =%lux\n", tag, rn, op&7, ur->psr>>28);
			return;
		}

		/*
		 * other transfer, 10.3
		 */
		switch((op>>20)&0xF){
		default:
			unimp(pc, op);
		case 0:	/* FLT */
			rn = (op>>16) & 7;
			fpiw2i(&FR(rn), &REG(rd));
			if(fpemudebug)
				print("MOVW[FD]	R%d, F%d\n", rd, rn);
			break;
		case 1:	/* FIX */
			if(op & (1<<3))
				unimp(pc, op);
			rn = op & 7;
			tmp = FR(rn);
			fpii2w(&REG(rd), &tmp);
			if(fpemudebug)
				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(rd));
			break;
		case 2:	/* FPSR := Rd */
			FPENV.status = REG(rd);
			if(fpemudebug)
				print("MOVW	R%d, FPSR\n", rd);
			break;
		case 3:	/* Rd := FPSR */
			REG(rd) = FPENV.status;
			if(fpemudebug)
				print("MOVW	FPSR, R%d\n", rd);
			break;
		case 4:	/* FPCR := Rd */
			FPENV.control = REG(rd);
			if(fpemudebug)
				print("MOVW	R%d, FPCR\n", rd);
			break;
		case 5:	/* Rd := FPCR */
			REG(rd) = FPENV.control;
			if(fpemudebug)
				print("MOVW	FPCR, R%d\n", rd);
			break;
		}
		return;
	}

	/*
	 * arithmetic
	 */

	if(op & (1<<3)){	/* constant */
		fm = &fpconst[op&7];
		tag = 'C';
	}else{
		fm = &FR(op&7);
		tag = 'F';
	}
	rd = (op>>12)&7;
	o = (op>>20)&0xF;
	if(op & (1<<15)){	/* monadic */
		FP1 *fp;
		fp = &optab1[o];
		if(fp->f == nil)
			unimp(pc, op);
		if(fpemudebug)
			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
		(*fp->f)(fm, &FR(rd));
	} else {
		FP2 *fp;
		fp = &optab2[o];
		if(fp->f == nil)
			unimp(pc, op);
		rn = (op>>16)&7;
		if(fpemudebug)
			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
		(*fp->f)(*fm, FR(rn), &FR(rd));
	}
}

/*
 * returns the number of FP instructions emulated
 */
int
fpiarm(Ureg *ur)
{
	ulong op, o;
	FPsave *ufp;
	int n;

#ifndef R13OK
/*	ur->type = &ur->pc+1;	/* calculate kernel sp/R13 and put it here for roff[13] */
	ur->type = (ulong)(ur + 1);
#endif
	if (up == nil)
		panic("fpiarm not in a process");
	ufp = &up->fpsave;
		/* because all the state is in the proc structure,
		 * it need not be saved/restored
		 */
	if(up->fpstate != FPACTIVE) {
		up->fpstate = FPACTIVE;
		ufp->control = 0;
		ufp->status = (0x01<<28)|(1<<12);	/* software emulation, alternative C flag */
		for(n = 0; n < 8; n++)
			FR(n) = fpconst[0];
	}
	for(n=0;;n++){
		op = getulong(ur->pc);
		o = (op>>24) & 0xF;
		if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC)
			break;
		if(condok(ur->psr, op>>28))
			fpemu(ur->pc, op, ur, ufp);
		ur->pc += 4;
	}
	return n;
}

A bitsy/fpimem.c => bitsy/fpimem.c +136 -0
@@ 0,0 1,136 @@
#include "fpi.h"

/*
 * the following routines depend on memory format, not the machine
 */

void
fpis2i(Internal *i, void *v)
{
	Single *s = v;

	i->s = (*s & 0x80000000) ? 1: 0;
	if((*s & ~0x80000000) == 0){
		SetZero(i);
		return;
	}
	i->e = ((*s>>23) & 0x00FF) - SingleExpBias + ExpBias;
	i->h = (*s & 0x007FFFFF)<<(1+NGuardBits);
	i->l = 0;
	if(i->e)
		i->h |= HiddenBit;
	else
		i->e++;
}

void
fpid2i(Internal *i, void *v)
{
	Double *d = v;

	i->s = (d->h & 0x80000000) ? 1: 0;
	i->e = (d->h>>20) & 0x07FF;
	i->h = ((d->h & 0x000FFFFF)<<(4+NGuardBits))|((d->l>>25) & 0x7F);
	i->l = (d->l & 0x01FFFFFF)<<NGuardBits;
	if(i->e)
		i->h |= HiddenBit;
	else
		i->e++;
}

void
fpiw2i(Internal *i, void *v)
{
	Word w, word = *(Word*)v;
	short e;

	if(word < 0){
		i->s = 1;
		word = -word;
	}
	else
		i->s = 0;
	if(word == 0){
		SetZero(i);
		return;
	}
	if(word > 0){
		for (e = 0, w = word; w; w >>= 1, e++)
			;
	} else
		e = 32;
	if(e > FractBits){
		i->h = word>>(e - FractBits);
		i->l = (word & ((1<<(e - FractBits)) - 1))<<(2*FractBits - e);
	}
	else {
		i->h = word<<(FractBits - e);
		i->l = 0;
	}
	i->e = (e - 1) + ExpBias;
}

void
fpii2s(void *v, Internal *i)
{
	short e;
	Single *s = (Single*)v;

	fpiround(i);
	if(i->h & HiddenBit)
		i->h &= ~HiddenBit;
	else
		i->e--;
	*s = i->s ? 0x80000000: 0;
	e = i->e;
	if(e < ExpBias){
		if(e <= (ExpBias - SingleExpBias))
			return;
		e = SingleExpBias - (ExpBias - e);
	}
	else  if(e >= (ExpBias + (SingleExpMax-SingleExpBias))){
		*s |= SingleExpMax<<23;
		return;
	}
	else
		e = SingleExpBias + (e - ExpBias);
	*s |= (e<<23)|(i->h>>(1+NGuardBits));
}

void
fpii2d(void *v, Internal *i)
{
	Double *d = (Double*)v;

	fpiround(i);
	if(i->h & HiddenBit)
		i->h &= ~HiddenBit;
	else
		i->e--;
	i->l = ((i->h & GuardMask)<<25)|(i->l>>NGuardBits);
	i->h >>= NGuardBits;
	d->h = i->s ? 0x80000000: 0;
	d->h |= (i->e<<20)|((i->h & 0x00FFFFFF)>>4);
	d->l = (i->h<<28)|i->l;
}

void
fpii2w(Word *word, Internal *i)
{
	Word w;
	short e;

	fpiround(i);
	e = (i->e - ExpBias) + 1;
	if(e <= 0)
		w = 0;
	else if(e > 31)
		w = 0x7FFFFFFF;
	else if(e > FractBits)
		w = (i->h<<(e - FractBits))|(i->l>>(2*FractBits - e));
	else
		w = i->h>>(FractBits-e);
	if(i->s)
		w = -w;
	*word = w;
}

M bitsy/main.c => bitsy/main.c +1 -1
@@ 229,7 229,7 @@ userinit(void)
void
procsetup(Proc *p)
{
	p->fpstate = FPinit;
	p->fpstate = FPINIT;
}

/*

M bitsy/screen.c => bitsy/screen.c +2 -2
@@ 297,8 297,8 @@ screenwin(void)
	grey = allocmemimage(Rect(0,0,1,1), RGB16);
	grey->flags |= Frepl;
	grey->clipr = gscreen->r;
	grey->data->bdata[0] = 0x77;
	grey->data->bdata[1] = 0x77;
	grey->data->bdata[0] = 0x40;
	grey->data->bdata[1] = 0xfd;

	w = memdefont->info[' '].width;
	h = memdefont->height;

M bitsy/trap.c => bitsy/trap.c +39 -3
@@ 16,7 16,6 @@ struct Intrregs
	ulong	icfp;	/* pending FIQs */
	ulong	dummy1[3];
	ulong	icpr;	/* pending interrupts */
	
};

struct Intrregs *intrregs;


@@ 35,7 34,6 @@ typedef struct Vctl {
static Lock vctllock;
static Vctl *vctl;


/*
 *   Layout at virtual address 0.
 */


@@ 267,14 265,52 @@ trap(Ureg *ureg)
		}
		break;
	case PsrMund:	/* undefined instruction */
#ifdef NOTDEF
		/* Inferno break handling, something for us? [SJM] */
		if(*(ulong*)ureg->pc == BREAK && breakhandler) {
			int s;
			Proc *p;

			p = up;
			/* if (!waslo(ureg->psr) || (ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo))
				p = 0; */
			s = breakhandler(ureg, p);
			if(s == BrkSched) {
				c.callsched = 1;
				sched();
			} else if(s == BrkNoSched) {
				c.callsched = 0;
				if(up)
					up->dbgreg = 0;
				return;
			}
			break;
		}
		/* End of Inferno break handling [SJM] */
#endif
		/* start of Inferno code [SJM] */
		spllo();
		if (waserror()) {
			warnregs(ureg, "floating point error");
			panic("floating point error");
		}
		if (!fpiarm(ureg)) {
			warnregs(ureg, "illegal instruction");
			panic("illegal instruction");
		}
		poperror();
		/* end of Inferno code [SJM] */
#ifdef NOTDEF
		/* We'll see what we do with this later [SJM] */
		if(user){
			sprint(buf, "sys: undefined instruction: pc 0x%lux\n",
			sprint(buf, "undefined instruction: pc 0x%lux\n",
					ureg->pc);
			postnote(up, 1, buf, NDebug);
		}else{
			warnregs(ureg, "undefined instruction");
			panic("undefined instruction");
		}
#endif
		break;
	}


M port/proc.c => port/proc.c +1 -1
@@ 327,7 327,7 @@ newproc(void)
	p->fgrp = 0;
	p->rgrp = 0;
	p->pdbg = 0;
	p->fpstate = FPinit;
	p->fpstate = FPINIT;
	p->kp = 0;
	p->procctl = 0;
	p->notepending = 0;