/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:fpa.c 12.3$ */
/* $ACIS:fpa.c 12.3$ */
/* $Source: /ibm/acis/usr/sys/ca/RCS/fpa.c,v $ */


#ifndef lint
static char *rcsid = "$Header:fpa.c 12.3$";
#endif

#include "debug.h"
#include "scr.h"
#include "mmu.h"
#include "reg.h"

#include "param.h"
#include "vmparam.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#include "proc.h"
#include "seg.h"
#include "acct.h"
#include "kernel.h"
#include "vmmeter.h"
#include "io.h"
#include "ioim.h"
#include "float.h"
#include "fp.h"
#include "fpa.h"
#include "fpavar.h"
#include "cpu.h"
#include "frame.h"
#include "syslog.h"

int fpa_num_regs = NUM_FPA_FLOATS;	/* How many registers to save */
int fpa_num_sets = NUM_FPA_SETS;

#ifdef FPA

#ifdef GOGO
#define DELAY(x)	fpa_delay(x)
int
fpa_delay(x)
int	x;
{
	while (--x >= 0);
	return(x);
}
#endif GOGO

/*
 * Variables used (mostly) internal to the fpa code.
 */

static void fpa_restore();

int
	fpa_count[FPA_COUNTERS];

static struct proc
	*fpa_proc[NUM_FPA_SETS];	/* current owner of this set */

static int
	fpa_nextgrab = 0;	/* next register to grab */

int	fpa_status;		/* Status of last exception */

static int fpa_regs = 0;		/* register allocation bit map */
#define	REG_SET_IN_USE(i)	(fpa_regs&(1<<(i)))
#define	REG_SET_FREE(i)		(fpa_regs &= ~(1<<(i)))
#define	REG_SET_ALLOCATE(i)	(fpa_regs |= 1<<(i))

static int
	fpa_unused = FPA_NONE;	/* What the default case is */

#define	FPA_CUR_NONE()		fpa_curreg = FPA_NONE
#define	FPA_CUR_UNUSED()	fpa_curreg = fpa_unused
#define	FPA_CUR_REGISTER(i)	fpa_curreg = (i)

/* The initial state values for the FPA */
static struct
				/* data Ilr	fpasr	ier rer  flags */
	fpa_state fpa_initial = { { 0 }, 0 , FP_S_unsgd, 0, 0,  FPA_EIGHTH_REG};

/* fpa exceptions in string form */
static char *fpa_tft[8] = {
    "no exception", "underflow", "overflow", "zero devide",
    "reserved", "operand error", "inexact result", "illegal command" };


#define LOADREGSET
#ifdef LOADREGSET
/*
 * Reserved register set contents (regset 24-31).  These are constants
 * used by AFPA ucode for on-chip transcendental functions.
 */
static long zeros[NUM_AFPA_FLOATS] = { 0 };

static long regset24[NUM_AFPA_FLOATS] = {
0x00000000, 0x00036000, 0x00016000, 0x0003F800,
0x00016000, 0x00016800, 0x00017000, 0x00017800,
0x0001E000, 0x0001E800, 0x0001F000, 0x0001F800,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00100000, 0x00000000, 0x00800000, 0x00000000,
0x40000040, 0x00000000, 0xFFFFEFFF, 0x40808000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xBFA8B9FD, 0xE410C874, 0x3FB5D9D9, 0x7886D172,
0xBFBC4F7F, 0x0223ED73, 0x3FC24865, 0xC0060469,
0xBFC99996, 0x886FF101, 0x3FD55555, 0x544E0375
};

static long regset27[NUM_AFPA_FLOATS] = {
0x00000000, 0x00000000, 0x3FC45F30, 0x6DC9C883,
0x401921FB, 0x40000000, 0x3E94442D, 0x18000000,
0x3FD45F30, 0x6DC9C883, 0x400921FB, 0x40000000,
0x3E84442D, 0x18000000, 0x00000000, 0x000FFFFF,
0x7FFFFFFF, 0xFFFFFFFF, 0x41800000, 0x00000000,
0x00000000, 0x00000000, 0x40038000, 0x00000000,
0x3FF30000, 0x3FE60000, 0x3FDC0000, 0x40080000,
0x3FEF730B, 0xD281F69B, 0xBC7C23DF, 0xEFEAE6B5,
0x3FDDAC67, 0x0561BB4F, 0x3C55543B, 0x8F253271,
0x43ABC16D, 0x674EC800, 0x3E112E0B, 0xE826D695,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000
};

static long regset28[NUM_AFPA_FLOATS] = {
0x00000000, 0x00000000, 0xBDA8BD59, 0x86B2A52E,
0x3E21EE3B, 0x60DDDC8C, 0xBE927E4F, 0x1314AD1A,
0x3EFA01A0, 0x1971CAEB, 0xBF56C16C, 0x16C14199,
0x3FA55555, 0x5555553E, 0x00000000, 0x40B00000,
0x40862000, 0x000003E8, 0x3BFD83C9, 0x4FB6D2AC,
0x00000000, 0x00000000, 0x7FF00000, 0x00000000,
0x400921FB, 0x54442D18, 0x3FF921FB, 0x54442D18,
0x3FE921FB, 0x54442D18, 0x4002D97C, 0x7F3321D2,
0x3FE00000, 0x00000000, 0x3FD0B70C, 0x6D604DD4,
0x3FF00000, 0x00000000, 0x3E49C511, 0xDC3A41DF,
0x3DE5CF61, 0xDF672B13, 0xBE5AE5C0, 0xE319A4EF,
0x3EC71DE3, 0x209CDCD9, 0xBF2A01A0, 0x19746345,
0x3F811111, 0x1110C461, 0xBFC55555, 0x5555550C,
0xFFFFFE7F, 0x54442D19, 0x3F015317, 0xDF4526C4,
0x3F8C70FF, 0x8B3CC2CF, 0x3F503FC4, 0xCB8C98E8,
0x3FBC7195, 0x38248597, 0x3FF71547, 0x652B82FE
};

static long regset29[NUM_AFPA_FLOATS] = {
0x00000000, 0x00000000, 0x000007FD, 0x00000000,
0x00100000, 0x00000000, 0x3FF6A09E, 0x667F3BCD,
0x41300000, 0x000003FF, 0x3DEA39EF, 0x35793C76,
0x3FE62E42, 0xFEE00000, 0x00000000, 0x40000000,
0x3FDBCB7B, 0x1526E50E, 0x40877AF8, 0xEBEAE354,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x3FC2F039, 0xF0085122, 0x3FC39A1E, 0xC014045B,
0x3FC74663, 0xCC94342F, 0x3FCC71C5, 0x2150BEA6,
0x3FD24924, 0x941E07B4, 0x3FD99999, 0x9997FF24,
0x3FE55555, 0x55555592, 0x3F90D521, 0x74A1BB54,
0xBFA2CA6C, 0x04C6937A, 0x3FA98605, 0x24BDD807,
0xBFADE125, 0xFDDBD793, 0x3FB10DA9, 0x24597FFF,
0xBFB3B0FA, 0x54BEC400, 0x3FB745CE, 0x5AA35DB2,
0xBFBC71C7, 0x059AF280, 0x3FC24924, 0x921EC1D7,
0xBFC99999, 0x99997CCD, 0x3FD55555, 0x555555C3,
0x7FF00000, 0x408FF800, 0xFFFFFE7F, 0x00000000
};

static long regset30[NUM_AFPA_FLOATS] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00400000, 0x7F800000, 0x7FF00000, 0x60008000,
0xBBBBBBBB, 0xAAAAAAAA, 0x90000000, 0x00000000,
0x00000000, 0xFFFFFFFF, 0x7FFFFFFF, 0x00080000,
0x7FC00000, 0x7FF80000, 0x00010000, 0x00033000,
0x7F800000, 0x7FF00000, 0x00012000, 0x0003F000,
0x00000C00, 0x00030C00, 0x00004800, 0x0003C800
};

static long regset31[NUM_AFPA_FLOATS] = {
0x00000000, 0x00000000, 0x00000004, 0x41000000,
0x00005800, 0xFFC00000, 0x41E00000, 0x43300000,
0x00001006, 0x00000000, 0x80000000, 0x7F800000,
0x7FF00000, 0x00000000, 0x00000000, 0x000FFFFF,
0x00028000, 0x00100000, 0x81000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00008000,
0x84000000, 0x80000020, 0x0003F800, 0x00012000,
0x7FF00000, 0x48008000, 0x84000800, 0x40008040,
0x80000820, 0xFFFF7FFF, 0x42008000, 0x81000800,
0xFFFFDFFF, 0x00000000, 0xFFFFFFF8, 0x00004000,
0x00000007, 0x00000005, 0xFFFFFFF8, 0x00000000,
0x00023C00, 0x00027000, 0x00002000, 0x00000010,
0x00000040, 0x7FFFFFFF, 0x0002DC00, 0x0003FC00,
0x00001000, 0xFFFFF9F8, 0x00000607, 0x00000000,
0x00000000, 0x00007000, 0x80000000, 0x00000180,
0x00000080, 0x00000000, 0xFFFFFE7F, 0xFFFF8FFF
};

static long	*registersets[NUM_FPA_SETS] = {
		zeros, zeros, zeros, zeros,
		zeros, zeros, zeros, zeros,
		zeros, zeros, zeros, zeros,
		zeros, zeros, zeros, zeros,
		zeros, zeros, zeros, zeros,
		zeros, zeros, zeros, zeros,
		regset24, zeros, zeros, regset27,
		regset28, regset29, regset30, regset31
		};
#endif LOADREGSET

/*
 * The following section consists of the code and variables necessary
 * to detect the fpa and afpa at system startup time.  These codes are
 * run during system init time.
 *
 * For the fpa, bring it online at system startup time. 
 *
 * For the afpa, just detect its presence, enable access to its control
 * storage (so a user program, afpacode(8), can load the micro-code at
 * later time), and exit.
 *
 * Review codes in this order:  fpa_init(), fpax_init(), afpa_init(),
 * fpa1_init().
 */

extern void (*_trap)();			/* Pointer to trap slih */

static label_t fpa_label;
/*
 * no_fpa may be patched to -1 (using kernel debugger) to ignored 
 * FPA during boot.
 *
 * no_diag_ucode may be patched to skip AFPA card level test
 *
 */
#define FPA_IGNORED			-1
static int
	no_fpa = 0,				/* assume we have FPAs */
	no_diag_ucode = 0,			/* assume we want diag_ucode */
	fpa_reset_done = 1,			/* 0 until fpa has been reset */
	fpa_mcs_pcs = 0;			/* what was the mcs_pcs? */

static enum fpa_trap_state {
    state_illegal, state_longjmp, state_return
} fpa_trap_state = state_illegal;

/*
 * trap intercept routine in effect during fpa initialization.
 * this is done so that we can test to see if the fpa is there, and
 * also to handle the machine check that the fpa produces if it is 
 * reset more than once (sigh).
 */

static void
fpa_trap(mcs_pcs, info, ics_cs, regs)
	register mcs_pcs, info;
	int ics_cs;			/* must not be a register variable */
	int regs;			/* must not be a register variable */
{
	register int *locr0 = &regs;

	fpa_mcs_pcs = mcs_pcs;		/* Save state */

	DEBUGF(fpadebug&SHOW_INIT,
	    prstate("fpa_trap", mcs_pcs, info, locr0[IAR], ics_cs, locr0));
#ifdef ROMPC
	DEBUGF ((fpadebug&SHOW_INIT) && cpu_model != CPU_SGP, {
	    int i;

	    for (i = IOIM1; i <= IOIM1+15; i++) {
		printf("0x%x", ior(i));
		if ((i%4) == 3) {
		    printf("\n");
		} else {
		    printf("\t");
		}
	    }
	});
#endif ROMPC
	if (mcs_pcs & MCS_CHECK && fpa_reset_done == 0) {
		fpa_reset_done = 1;
		printf("FPA caused machine check ignored\n");
		klsreset();     /* reinitialize keyboard */
		klswait();      /* and wait until done */
		return;		/* try it again */
	}
	switch (fpa_trap_state) {
	case state_illegal:
		panic("fpa_trap:  illegal trap\n");
		/*NOTREACHED*/
	case state_longjmp:
		longjmp(&fpa_label);
		/*NOTREACHED*/
	case state_return:
		return;
	default:
		panic("fpa_trap:  illegal value in fpa_trap_state\n");
		/*NOTREACHED*/
	}
}

/*
 * This routine is called if fpax_init() decides we are running with
 * the fpa1.
 */
static void
fpa1_init()
{
	register int i, x;

	DEBUGF(fpadebug&SHOW_INIT, printf("Enter fpa1_init\n"));

	x = fpa_rdscx();
	DEBUGF(fpadebug&SHOW_INIT, printf("fpa1_init: after rdscx\n"));
	DELAY(10);			/* force delay */

	fpa_lockfp();
	DEBUGF(fpadebug&SHOW_INIT, printf("fpa1_init: after lockfp\n"));
	DELAY(10);			/* force delay */
/* 
 * for each register set do a taskswitch and read status & clear
 * exception and then write the status register.
 */
	for (i=0; i<NUM_FPA_SETS; ++i) {
		fpa_tskswu(i);
		DELAY(10);
		x = fpa_rdscx();
		DELAY(10);
		fpa_wtstr(0);
		DELAY(10);
		if (fpa_rdstr() != 0) {
			printf("FPA fails 'zero status' test.  Marked down.\n");
			return;
		}
	}
	if (!fpa_test()) {
		fpa_lockfp();		/* lock the register set again */
		printf("FPA fails basic arithmetic test.  Marked down.\n");
		return;
	}

#ifdef ROMPC
#ifdef SGP
#ifdef MOD135
    if ((cpu_model == CPU_ROMPC) || (cpu_model == CPU_MOD135))
#else
    if (cpu_model == CPU_ROMPC)
#endif MOD135
#endif SGP
	{
	/* Now, enable user access to subsegment F */
	x = ior(IOIM1+IOIM_SUB_PROB);		/* Get old value */
	iow(IOIM1+IOIM_SUB_PROB, x | SEG_F);	/* Turn on user access */
	}
#endif ROMPC

	fpa_lockfp();

	/* FPA present */
	proc[0].p_fpareg = fpa_unused = FPA_UNUSED;
	FPA_CUR_UNUSED();
	fpa_num_regs = NUM_FPA_FLOATS;
	float_hardware |= FLOAT_FPA;
	printf("FPA initialized %s\n",
		fpa_reset_done != 2 ? "normally" : "(after machine check)");
	return;
}

#define FLOAT_ONE		0x3f800000
#define FLOAT_TWO		0x40000000

static int	afpa_diag_ucode_hi[47] = {
			0x39800020, 0x07080000, 0x0E003FF0, 0x0B000000,
			0x13800000, 0x09000C00, 0x03800000, 0x0F800000,
			0x0B800000, 0x0EA00240, 0x06800000, 0x06800000,
			0x06800000, 0x06800000, 0x06800000, 0x06800000,
			0x06800000, 0x06800000, 0x06800000, 0x06800000,
			0x00800000, 0x0980F070, 0x1B000000, 0x00007C0A,
			0x3DE0F00A, 0x3DE0F00A, 0x3DE0F00A, 0x3980018A,
			0xC0450000, 0x3B870000, 0x398001FE, 0x3980020A,
			0x39800219, 0x00000009, 0xF86A000A, 0x320703FA,
			0x3980018E, 0x3D870000, 0x3980018A, 0x39800180,
			0x00000000, 0x0000000A, 0x39800180, 0x398001C0,
			0x39800280, 0x39800270, 0x39800258
			};
static int	afpa_diag_ucode_lo[47] = {
			0x01210000, 0x00000010, 0x01000000, 0x01000030,
			0x010000D0, 0x01000050, 0x01000070, 0x010000B8,
			0x010000A0, 0x01000000, 0x010000B0, 0x01000008,
			0x01000018, 0x01000080, 0x01000010, 0x01000000,
			0x01000000, 0x01000000, 0x01000000, 0x01000090,
			0xA1000000, 0x01000000, 0x01000078, 0xA0000000,
			0xA10800B8, 0xA1080098, 0xA1080008, 0xA1080000,
			0x01000000, 0x91000000, 0x01540000, 0xA1080000,
			0x81400000, 0x8D40F088, 0xA1103FF0, 0x11080078,
			0xA1540090, 0xA1000008, 0xA1080090, 0x01000090,
			0xA1000020, 0x01080008, 0xA1000090, 0x9120AFA0,
			0x014000A0, 0x010000A0, 0x014100A0
			};
/*
 * Tesing for AFPA pass_c or pass_d:
 *
 * Write float 1 to reg0.  Write using these diag ucode will
 * first write, and then add the number to itself.  Pass_c
 * use ucode to write, so will read back twice what is written.
 * Pass_d doesn't.
 */
void
afpa_init()
{
	register void	(*oldtrap)() = _trap;
	register int	i, x;

	DEBUGF(fpadebug&SHOW_INIT, printf("Enter afpa_init\n"));

	/* Do NOT set 'FLOAT_AFPA' until microcode loaded (afpacode(8)) */
	fpa_num_regs = NUM_AFPA_FLOATS;
	float_hardware |= FLOAT_AFPA_HARDWARE;

	if (no_diag_ucode) {	/* skip AFPA card level test */
		float_hardware |= FLOAT_AFPA_CONTROL_STORE_ENABLE;
		printf("(E_)AFPA marked down pending microcode load and initialization.\n");
		return;
	}

	_trap = fpa_trap;
	fpa_trap_state = state_longjmp;
	/*
	 * if we trap, for any reasons, just mark the AFPA hardware
	 * is bad and forget about it.
	 */
	if (setjmp(&fpa_label)) {
		printf("(E_)AFPA fails diag test.  Marked down.\n");
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		klsreset();
		return;
	}
	for (i=0; i<4096; i++) {	/* zero-ing out ucode */
		AFPA_WRITE_UCODE_HIGH(0,i);
		AFPA_WRITE_UCODE_LOW(0,i);
	}
	DEBUGF(fpadebug&SHOW_INIT,
		printf("afpa_init: 4096 ucode words zero-ed\n"));

	for (i=0; i<43; i++) {		/* load diag ucode for afpa */
		AFPA_WRITE_UCODE_HIGH(afpa_diag_ucode_hi[i],i);
		AFPA_WRITE_UCODE_LOW(afpa_diag_ucode_lo[i],i);
	}
	DEBUGF(fpadebug&SHOW_INIT,
		printf("afpa_init: 43 diag ucode words loaded\n"));

	/* some special location for ucode */
	AFPA_WRITE_UCODE_HIGH(afpa_diag_ucode_hi[43],3988);
	AFPA_WRITE_UCODE_LOW( afpa_diag_ucode_lo[43],3988);
	AFPA_WRITE_UCODE_HIGH(afpa_diag_ucode_hi[44],3992);
	AFPA_WRITE_UCODE_LOW( afpa_diag_ucode_lo[44],3992);
	AFPA_WRITE_UCODE_HIGH(afpa_diag_ucode_hi[45],3999);
	AFPA_WRITE_UCODE_LOW( afpa_diag_ucode_lo[45],3999);
	AFPA_WRITE_UCODE_HIGH(afpa_diag_ucode_hi[46],4028);
	AFPA_WRITE_UCODE_LOW( afpa_diag_ucode_lo[46],4028);
	DEBUGF(fpadebug&SHOW_INIT,
		printf("afpa_init: 47 diag ucode words loaded\n"));

	afpa_disable_control_store();	/* MACRO */
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_init: disable_control\n"));
	DELAY(10);			/* force delay */

#ifdef GOGO
	afpa_diag_tskswu(0);
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_init: afpa_diag_tskswu\n"));
	DELAY(10);			/* force delay */
#endif GOGO

	fpa_tskswu(0);
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_init: fpa_tskswu\n"));
	DELAY(10);			/* force delay */

	afpa_write_register(0, FLOAT_ONE);	/* float 1 to reg 0 */
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_init: written\n"));
	DELAY(10);			/* force delay */

	i = afpa_read_register(0);		/* read it back */
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_init: read back i = %x\n",i));
	DELAY(10);			/* force delay */

	afpa_enable_control_store();	/* MACRO */
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_init: enable_control\n"));
	DELAY(10);			/* force delay */

	/* just to be sure we can touch the ucode */
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_init: before AFPA_UCODE_HIGH\n"));
	AFPA_READ_UCODE_HIGH(x,0);

	if ((i == FLOAT_TWO) || (i == 0)) {
		/* 
		 * for some reason, the first time power on reboot will have
		 * i=FLOAT_TWO, but there after on software reboot, we will
		 * have i=0.  In both case the hardware is working fine!
		 */
		float_hardware |= FLOAT_AFPA_CONTROL_STORE_ENABLE;
		printf("AFPA marked down pending microcode load and initialization.\n");
	} else if (i == FLOAT_ONE) {
		float_hardware |= (FLOAT_AFPA_PASS_D|FLOAT_AFPA_CONTROL_STORE_ENABLE);
		printf("E_AFPA marked down pending microcode load and initialization.\n");
	} else {
		printf("afpa_init, read back result = 0x%x\n",i);
		printf("(E_)AFPA fails card level test.  Marked down.\n");
	}

	fpa_trap_state = state_illegal;
	_trap = oldtrap;
	return;
}

/*
 *
 * We got here because there was an exception while trying to read
 * AFPA_UCODE_HIGH.  It could be in one of these conditions:
 *
 * 1.  No FPAs installed.
 * 2.  FPA1 is installed.
 * 3.  AFPA is installed BUT the user reboot by ctrl-alt-pause, thus
 *	the AFPA is in control-store-disable state.  Trying to read
 *	AFPA_UCODE_HIGH in this state caused the exception.
 *
 * DELAY() was used liberally here.  It is because that the MOD 135
 * APC seems easily overrun the FPA/AFPA if we have several fpa
 * instruction on a row.  DELAY(10) may be over-kill, but I know
 * DELAY(5) is not long enough.
 *
 */
void
fpax_init()
{
	register void (*oldtrap)() = _trap;
	register int x;

	DEBUGF(fpadebug&SHOW_INIT, printf("Enter fpax_init\n"));

	/* assume no FPA present */
	FPA_CUR_NONE();
	proc[0].p_fpareg = fpa_unused = FPA_NONE;
	fpa_num_regs = NUM_FPA_FLOATS;

	_trap = fpa_trap;
	fpa_trap_state = state_longjmp;

	/*
	 * test for fpa1 present by doing a fpa_rdscx.  If we trap,
	 * there is no FPAs.
	 */
	if (setjmp(&fpa_label)) {	/* no FPA present */
		FPA_CUR_NONE();
		proc[0].p_fpareg = fpa_unused = FPA_NONE;
		printf("Neither AFPA nor FPA present.\n");
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		return;
	}
	x = fpa_rdscx();	/* clr exception on current reg set */
	DEBUGF(fpadebug&SHOW_INIT, printf("fpax_init: after rdscx\n"));
	DELAY(10);			/* force delay */

	fpa_tskswu(0);		/* switch to reg set 0 */
	DEBUGF(fpadebug&SHOW_INIT, printf("fpax_init: after tskswu\n"));
	DELAY(10);			/* force delay */

	x = fpa_rdscx();	/* clr exception again just to be sure */
	DEBUGF(fpadebug&SHOW_INIT, printf("fpax_init: after rdscx\n"));
	DELAY(10);			/* force delay */

	/*
	 * now, is it fpa1 or afpa?  fpa1 will not take control
	 * store enable
	 */
	if (setjmp(&fpa_label)) {	/* if we trap, it is fpa1 */
	DEBUGF(fpadebug&SHOW_INIT, printf("fpax_init: before fpa1_init()\n"));
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		fpa1_init();		/* Go check for fpa1 */
		return;
	}

	DEBUGF(fpadebug&SHOW_INIT, printf("fpax_init: before enacs\n"));
	afpa_enable_control_store();	/* MACRO */
	DELAY(10);			/* force delay */

	/* just to be sure you can touch the ucode */
	DEBUGF(fpadebug&SHOW_INIT, printf("fpax_init: before AFPA_UCODE_HIGH\n"));
	AFPA_READ_UCODE_HIGH(x,0);

	fpa_trap_state = state_illegal;
	_trap = oldtrap;
	afpa_init();
	return;
}


/*
 * initialize the FPA, including determining if it is present.
 * we determine if it is present by trapping faults (via _trap)
 * and marking the fpa not present.
 *
 * For the afpa, we just detect it, and then exit (since the micro
 * code needs to be loaded before we do anything exciting to it).
 *
 * note that this routine MUST be invoked before any fork's have
 * been done in order to set p_fpareg appropriately.
 * WARNING:
 *	it is arranged that fpa_curreg = FPA_NONE, and
 *		proc[0].p_fpareg = FPA_NONE
 *	during startup. This prevents 'resume' from changing the fpa 
 *	register set or locking the fpa. Changing fpa_curreg or p_fpareg's 
 *	value is done here (in sync) to FPA_NONE if there is no fpa, or to
 *	FPA_UNUSED (fpa present, not being used by this process).
 */
fpa_init()
{
	register void (*oldtrap)() = _trap;
	register int x;

	/* assume no FPA present */
	FPA_CUR_NONE();
	proc[0].p_fpareg = fpa_unused = FPA_NONE;
	if (no_fpa == FPA_IGNORED) { /*set by kernel debugger to ignore FPAs*/
		printf("FPA ignored\n");
		return;
	}

	_trap = fpa_trap;
	fpa_trap_state = state_longjmp;

	/*
	 * What kind of FPAs is out there?
	 *
	 * First, assume we have some type of AFPA.  At power-on time,
	 * AFPA is in control-store-enable state, test for it by reading
	 * control store high, location 0
	 */
	if (setjmp(&fpa_label)) {	/* if we trap, see fpax_init() */
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		fpax_init();
		return;
	}

	DEBUGF(fpadebug&SHOW_INIT,printf("fpa_init: before AFPA_UCODE_HIGH\n"));
	AFPA_READ_UCODE_HIGH(x,0);

	/*
	 * so we didn't trap, it is AFPA.  Is it AFPA (pass_c)
	 * or E_AFPA (pass_d)?
	 */
	fpa_trap_state = state_illegal;
	_trap = oldtrap;
	afpa_init();
	return;
}

/*
 * The following section consists of code that are used mainly by
 * a user program afpacode(8) to bring the afpa online.
 *
 * These codes are run in multi user mode.  We alter _trap to handle
 * any exceptions that may come out of the afpa.  For that reason,
 * we have to set spl to the highest level.
 */

/*
 * Respond to a user (superuser) request to enable the afpa's
 * control store.  In this (new) state, the user program will
 * be able to load (and dump) the afpa's control store.
 *
 * At power-on time, AFPA is already in cs-enable state.  If
 * the user reboot by ctrl-alt-pause, then fpax_init() and
 * afpa_init() have made sure that the AFPA is put back into
 * this state.   This function is used mainly by afpacode(8)
 * in the interactive mode to debug the AFPA.
 */
static int
afpa_enable_control()
{
	register void (*oldtrap)() = _trap;
	int s = spl7();		/* block all interrupts */
	int x;

	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_enable_control\n"));

	_trap = fpa_trap;
	fpa_trap_state = state_longjmp;

	if (setjmp(&fpa_label)) {
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		klsreset();     /* reinitialize keyboard */
		splx(s);	/* allow intr to come in */
		klswait();      /* and wait until done */
		log(LOG_CRIT, "AFPA unable to enable control store.\n");
		return 0;
	}

	afpa_enable_control_store();	/* MACRO */
	DEBUGF(fpadebug&SHOW_INIT,
	printf("afpa_enable_control: after enable_control\n"));
	DELAY(10);			/* force delay */

	/* Test to see if it worked */
	AFPA_READ_UCODE_HIGH(x,0);
	DEBUGF(fpadebug&SHOW_INIT,
	printf("afpa_enable_control: after AFPA_UCODE_HIGH\n"));

	fpa_trap_state = state_illegal;
	_trap = oldtrap;
	splx(s);
	return 1;
}

/*
 * The user (superuser) has requested that access to the afpa's control
 * store be disabled.  This automatically starts the afpa running.
 *
 * What we need to do is:
 *	o	Disable Control Store enable (this starts the microcode).
 *
 * After we finish, the user code still needs to:
 *	o	Initialize the "reserved" registers.
 *	o	Let us know how many registers are reserved for use
 *		by the microcode.
 */

static int
afpa_disable_control()
{
	register void (*oldtrap)() = _trap;
	int s = spl7();
	int i,j;

	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_disable_control\n"));

	_trap = fpa_trap;
	fpa_trap_state = state_longjmp;

	if (setjmp(&fpa_label)) {
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		klsreset();
		splx(s);
		klswait();
		log(LOG_CRIT, "AFPA control store disable failed.\n");
		return 0;
	}

	afpa_disable_control_store();	/* MACRO */

	/*
	 * Now, zero out all the registers.
	 */
	if (setjmp(&fpa_label)) {
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		klsreset();
		splx(s);
		klswait();
		log(LOG_CRIT, "AFPA:  Unable to zero register sets.\n");
		return 0;
	}
	DEBUGF(fpadebug&SHOW_REGS, printf("afpa_disable_control: zero-ing register set:\n"));
	for (i = 0; i < NUM_FPA_SETS; i++) {
		DEBUGF(fpadebug&SHOW_REGS, printf(" %d",i));
		afpa_diag_tskswu(i);
		DELAY(10);
		for (j = 0; j < NUM_AFPA_FLOATS; j++) {
			afpa_write_register(j, 0);
			DELAY(10);
		}
	}
	DEBUGF(fpadebug&SHOW_REGS, printf("\n"));
	afpa_diag_tskswu(0);		/* switch back to regset 0 */
	DELAY(10);

#ifdef LOADREGSET
	/*
	 * Load ucode constants into reserved regset
	 */
	if (setjmp(&fpa_label)) {
		fpa_trap_state = state_illegal;
		_trap = oldtrap;
		klsreset();
		splx(s);
		klswait();
		log(LOG_CRIT, "AFPA: Unable to load reserved register sets.\n");
		return 0;
	}
	for (i = 0; i < NUM_FPA_SETS; i++) {

		long *regs = registersets[i];

		if (regs != zeros) {
			DEBUGF(fpadebug&SHOW_REGS, printf("afpa_disable_control: loading register set %d\n",i));
			fpa_tskswu(i);
			DELAY(10);
			for (j = 0; j < NUM_AFPA_FLOATS; j++) {
				afpa_write_register(j, *regs);
				DELAY(10);
				regs++;
			}
		}
	}
#endif LOADREGSET

#ifdef ROMPC
#ifdef SGP
#ifdef MOD135
    if ((cpu_model == CPU_ROMPC) || (cpu_model == CPU_MOD135))
#else
    if (cpu_model == CPU_ROMPC)
#endif MOD135
#endif SGP
	{
	/* Now, enable user access to subsegments E and F */
	i = ior(IOIM1+IOIM_SUB_PROB);		/* Get old value */
	iow(IOIM1+IOIM_SUB_PROB, i | SEG_E | SEG_F);/* Turn on user access */
	}
#endif ROMPC

	fpa_trap_state = state_illegal;
	_trap = oldtrap;
	splx(s);
	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_disable_control (i=%x) - return 1\n",i));
	return 1;
}

/*
 * Here to bring the afpa online.
 */

static int
afpa_online()
{
	register void (*oldtrap)() = _trap;
	int x, i;
	int s = spl7();

	DEBUGF(fpadebug&SHOW_INIT, printf("afpa_online\n"));

	_trap = fpa_trap;
	fpa_trap_state = state_longjmp;
	/* 
	 * for each register set do a taskswitch and read status & clear
	 * exception and then write the status register.
	 */
	DEBUGF(fpadebug&SHOW_INIT, printf("fpa_num_sets = %d\n", fpa_num_sets));
	for (i=0; i < fpa_num_sets; i++) {
		if (setjmp(&fpa_label)) {
			fpa_trap_state = state_illegal;
			_trap = oldtrap;
			klsreset();
			splx(s);
			klswait();
			log(LOG_CRIT, "AFPA error switching register sets.\n");
			return 0;
		}
		fpa_tskswu(i);
		if (setjmp(&fpa_label)) {
		    if (fpa_mcs_pcs & MCS_DATA_TO) {
			fpa_trap_state = state_illegal;
			_trap = oldtrap;
			klsreset();
			splx(s);
			klswait();
			log(LOG_CRIT,
			"AFPA error reading and clearing status register.\n");
			return 0;
		    }
		    /* If no timeout, keep trying the reset... */
		}
		x = fpa_rdscx();
		if (setjmp(&fpa_label)) {
			fpa_trap_state = state_illegal;
			_trap = oldtrap;
			klsreset();
			splx(s);
			klswait();
			log(LOG_CRIT,"AFPA error resetting status register.\n");
			return 0;
		}
		fpa_wtstr(0);
		if ((x = fpa_rdstr()) != 0) {
			fpa_trap_state = state_illegal;
			_trap = oldtrap;
			splx(s);
			DEBUGF(fpadebug&SHOW_INIT, printf("AFPA register set %d failed 'zero status' test, status = %x.\n", i, x));
			log(LOG_CRIT,
			"AFPA register set failed 'zero status' test.\n");
			return 0;
		}
	}
	fpa_trap_state = state_illegal;
	_trap = oldtrap;
	splx(s);
	proc[0].p_fpareg = fpa_unused = FPA_UNUSED;
	FPA_CUR_UNUSED();
	return 1;
}

/*
 * The following are the internal routines called by the externally-callable
 * fpa (and afpa) routines.
 *
 * They are listed in alphabetical order.
 */

/*
 * allocate a register set to the current process
 * 1. find a set
 * 2. set it up
 */

static void
fpa_alloc()
{
	static void fpa_register_set();

	fpa_register_set(fpa_findreg());
}

/*
 * Bump the current owner of a register set.
 */

static void
fpa_bump(set)
int	set;
{
	register struct proc *p;
	void fpa_save();
	register int sts;

	DEBUGF(fpadebug&SHOW_REGS,
			printf("fpa_bump: bumping register set %d\n",set));
	p = fpa_proc[set];
	if ((p == 0) || (p->p_fpareg != set)) {
		panic("fpa_bump");	/* oops */
	}
	fpa_tskswu(set);		/* make it current */
	sts = fpa_rdscx();
			/* Save the register set.  Don't access user space */
	fpa_save((struct fpa_state *)floatsave[p-proc].fpa_intreg, p, 0);
	REG_SET_FREE(p->p_fpareg);
	p->p_fpareg = FPA_SAVED;	/* we saved it */
	FPA_CUR_UNUSED();
	++fpa_count[FPA_BUMP];			/* count registers grabbed */
}

/*
 * locate a free register if possible - otherwise steal one
 */
static int
fpa_findreg()
{
	register int i, sts;
	
	for (i = 0; i < fpa_num_sets; ++i) {
		if (!REG_SET_IN_USE(i))  {
			break;
		}
	}
	if (i == fpa_num_sets) {
	    i = fpa_grabreg();
	}
	DEBUGF(fpadebug&SHOW_REGS, printf("fpa_findreg: pid %d reg set %d\n",
		u.u_procp->p_pid,i));
	fpa_tskswu(i);			/* switch to it */
	sts = fpa_rdscx();
	return i;
}

/*
 * grab a register set from some other process.
 */
static int
fpa_grabreg()
{
	register int set = fpa_nextgrab;

	if (++set >= fpa_num_sets) {
		set = 0;
	}
	fpa_nextgrab = set;
	DEBUGF(fpadebug&SHOW_REGS,
			printf("fpa_grabreg: grabbing register set %d\n",set));
	fpa_bump(set);
	++fpa_count[FPA_GRAB];			/* count registers grabbed */
	REG_SET_ALLOCATE(set);
	return set;
}

/*
 * fpa_register_set(p, i)
 *
 * Set up register set i for the indicated process
 * (including loading the correct contents into registers)
 */

static void
fpa_register_set(i)
int i;
{
	struct proc *p = u.u_procp;
	int oldset = p->p_fpareg;

	REG_SET_ALLOCATE(i);		/* Allocate this set */
	u.u_floatmask |= (float_hardware&(FLOAT_FPA|FLOAT_AFPA|FLOAT_AFPA_DMA|FLOAT_AFPA_PASS_D));
	p->p_fpareg = i;
	FPA_CUR_REGISTER(i);
	fpa_proc[i] = p;		/* remember who has it */
	fpa_restore((oldset == FPA_SAVED) ?
	    (struct fpa_state *)floatsave[p-proc].fpa_intreg: &fpa_initial, 0);
}


/*
 * Restore the current register set from restore area provided
 *
 * As a side-effect, we may restore the eighth register to
 * the user area (in the case of an fpa).
 */

static void
fpa_restore(restorefp, save_eighth)
struct fpa_state *restorefp;
int	save_eighth;
{
	int i;
	u_int *data = &restorefp->programmer.data[0];

	if (fpa_num_regs <= NUM_FPA_FLOATS) {	/* FPA1 mode */
		int volatile *FPA_ptr = (int volatile *) (FPA_BASE + 0x025000);

		for (i=0; i < fpa_num_regs; ++i) {
			* FPA_ptr = *data++;
			FPA_ptr += 1;		/* actually add 2^2 or 4 */
		}
		/*
		 * On an fpa, restore the eighth register to the USER_FPM.
		 */
		if (save_eighth & (restorefp->flags&FPA_EIGHTH_REG)) {
			((FP_MACH *)USER_FPM)->dreg[NUM_FPA_FLOATS/2].dfracth =
				   restorefp->programmer.data[NUM_FPA_FLOATS];
			((FP_MACH *)USER_FPM)->dreg[NUM_FPA_FLOATS/2].dfractl =
				   restorefp->programmer.data[NUM_FPA_FLOATS+1];
		}
		fpa_wtier(restorefp->ier);	/* AFPA doesn't support this */
	} else {
		for (i=0; i < fpa_num_regs; ++i) {
			afpa_write_register(i, *data);
			DELAY(10);
			data++;
		}
	}
	fpa_wtstr(restorefp->fpasr);		/* store status */
}


/*
 * Save the current register set into save area provided
 *
 * If the current process is using the fpa, and if we have
 * access to the user stack (ie: can afford to take a page hit
 * to page it in), then save the eighth double register (maintained
 * by the code generator).
 *
 * Since we are sometimes called only to make sure the eighth soft
 * register is updated, we check the process structure to see if
 * we should actually save the hardware registers.
 */

static void
fpa_save(savefp, p, save_eighth)
struct fpa_state *savefp;
struct proc *p;
int	save_eighth;			/* Are we running in the user context */
{
	int i;
	u_int *data = &savefp->programmer.data[0];

	if (fpa_hasreg(p->p_fpareg)) {
		savefp->fpasr = fpa_rdstr();		/* store status */
		if (fpa_num_regs <= NUM_FPA_FLOATS) {
		    int volatile *FPA_ptr = (int volatile *) (FPA_BASE + 0x02f000);

		    savefp->ier = fpa_rdier();	/* AFPA doesn't support this */
		    for (i=0; i < fpa_num_regs; ++i) {
			    *data++ = * FPA_ptr;
			    FPA_ptr += 16;	/* actually add 2^6 or 64 */
		    }
		} else {
		    for (i=0; i < fpa_num_regs; ++i) {
			    *data++ = afpa_read_register(i);
			    DELAY(10);
		    }
		}
	}
	if ((fpa_num_regs <= NUM_FPA_FLOATS)
		&& (fpa_hasreg(p->p_fpareg) || (p->p_fpareg == FPA_SAVED))) {
		/*
		 * If we are running on the user's stack, and if this is
		 * an fpa user (as opposed to an afpa user), then
		 * pull in the software (code generator) maintained
		 * eighth data register.
		 */
		if (save_eighth && float_has_fpa(u.u_floatmask)) {
			savefp->programmer.data[NUM_FPA_FLOATS] =
			  ((FP_MACH *)USER_FPM)->dreg[NUM_FPA_FLOATS/2].dfracth;
			savefp->programmer.data[NUM_FPA_FLOATS+1] =
			  ((FP_MACH *)USER_FPM)->dreg[NUM_FPA_FLOATS/2].dfractl;
			savefp->flags |= FPA_EIGHTH_REG;
		} else {
			/* If we can't save the state, note that fact */
			savefp->flags &= ~FPA_EIGHTH_REG;
		}
	}
}

/*
 * do FPA unit test to determine if FPA is operational.
 */
static int
fpa_test()
{
	int result;

	* (int volatile *) 0xff025000 = 0x3f800000;	/* fr0 = 1.0 */
	* (int volatile *) 0xff090480 = 0x40000000;	/* fr0 += (fr2 = 2.0) */

	result = * (int volatile *) 0xff02f000;		/* result = fr0 */
	return(result == 0x40400000);
}

/*
 * The following routines, presented in alphabetical order, comprise
 * the set of routines called from float.c.
 */


/*
 * realize the fpa register set into user address space for inspection
 * by debugger. 
 * if the fpa is present and allocated we store it into the emulator
 * data structure on the top of the user stack
 * if saved we copy the saved structure.
 */
fpa_core()
{
	register struct proc *p = u.u_procp;	/* my proc entry */
	register int fp_reg = p->p_fpareg;
	struct fpa_state *state =(struct fpa_state *)&floatsave[p-proc];

	if (fpa_hasreg(fp_reg))	{ /* has register set */
		register int status = fpa_rdscx();	/* paranoia */
	}
	fpa_save(state, p, 1);		/* Always save */
	bcopy((caddr_t) state, (caddr_t) (USER_FPM), sizeof *state);
}


/*
 * invoked at exit or exec to release a register set
 */
fpa_exit(p)
register struct proc *p;
{
	register int reg = p->p_fpareg;

	DEBUGF(fpadebug&SHOW_INIT,
	printf("Enter fpa_exit: u.u_floatmask %x.\n",u.u_floatmask));
	if (fpa_hasreg(reg)) {
		register int status = fpa_rdscx();	/* paranoia */
		DEBUGF(fpadebug&SHOW_REGS,
			printf("fpa_exit: release pid %d regs %d (status %x)\n",
				p->p_pid,reg, status));
		fpa_lockfp();
		REG_SET_FREE(reg);		/* release register set */
		fpa_proc[reg] = 0;		/* forget it */
	}
	p->p_fpareg = fpa_unused;		/* mark as unused */
	FPA_CUR_UNUSED();
	u.u_floatmask &= ~(FLOAT_FPA|FLOAT_AFPA|FLOAT_AFPA_DMA|FLOAT_AFPA_PASS_D);
	DEBUGF(fpadebug&SHOW_INIT,
	printf("fpa_exit: u.u_floatmask %x.\n",u.u_floatmask));
}

/*
 * fork -
 *
 *	Save the old register set.  This will save the actual hardware
 * registers if the old register set was in the hardware.  Else,
 * it will just update the eighth (software) register.  Then, copy
 * the old area into the new area.
 */

void
fpa_fork(oldp,newp)
register struct proc *oldp, *newp;
{
	struct fpa_state *newsavep = (struct fpa_state *) &floatsave[newp-proc];
	struct fpa_state *oldsavep = (struct fpa_state *) &floatsave[oldp-proc];

	/*
	 * Don't do the assignment if we are dealing with someone who
	 * doesn't have anything (since when we do the initial system
	 * fork(s) at init_main time we can't afford to take page
	 * faults, etc.).
	 */
	if (fpa_hasreg(oldp->p_fpareg) || (oldp->p_fpareg == FPA_SAVED)) {
		fpa_save(oldsavep, oldp, 0);	/* Fork already copied 8th reg */
		*newsavep = *oldsavep;
	}
	if (fpa_hasreg(oldp->p_fpareg)) {
		newp->p_fpareg = FPA_SAVED;
	} else {
		newp->p_fpareg = oldp->p_fpareg;
	}
}

/*
 * utility routine to get floating point register contents
 * (invoked from ptrace)
 * return:	0	ok
 *		~0	failed
 * note that if an FPA register set has not yet been allocated
 * referencing the FPA will cause one to be.
 */

int
fpa_getreg(reg,value)
u_int reg;
int *value;
{
	struct proc *p = u.u_procp;
	struct fpa_state *newsavep = (struct fpa_state *) &floatsave[p-proc];

	fpa_save(newsavep, p, 1);

	if (fpa_hasreg(p->p_fpareg)) {	/* has register set */
		REG_SET_FREE(p->p_fpareg);
		fpa_lockfp();			/* Lock the FPA */
		p->p_fpareg = FPA_SAVED;	/* mark as saved */
		FPA_CUR_UNUSED();
	}
	if ((reg < fpa_num_regs) ||
		(float_has_fpa(u.u_floatmask) && (reg < NUM_FPA_FLOATS+2))) {
		*value = newsavep->programmer.data[reg];
		return 0;		/* succeeded */
	} else {
			    /* invalid register number */
		if (float_has_fpa(u.u_floatmask)) {
			return NUM_FPA_FLOATS+1;
		} else {
			return fpa_num_regs;
		}
	}
}

/*
 * trap has detected a invalid data reference that might be 
 * fpa related.
 * we return 0 if it isn't ours (fpa reg set already allocated)
 *	     1 if we have now allocated register set
 * to be done:
 *	check to see if the reference is to '0xff......'
 */
fpa_pci(mcs_pcs,info,locr0)
	register int mcs_pcs;
	int info;
	register int *locr0;
{
	extern short resume_rdstr;	/* read status instruction */
	static level = 0;	/* to detect recursive fpa exceptions */
	register struct proc *p = u.u_procp;
	register int fp_reg = p->p_fpareg;
	register int fixup_rc;

	if ((GETSEG(info) != GETSEG(FPA_BASE)) ||
		    ((GETSUBSEG(info) != GETSUBSEG(FPA_BASE))
		    && (GETSUBSEG(info) != GETSUBSEG(FPA_DMA_BASE)))) {
	    return 0;		/* Not ours */
	}

	DEBUGF(fpadebug&SHOW_PCI,
		prstate("fpa_pci", mcs_pcs, info, locr0[IAR], locr0[ICSCS],locr0));
	if (float_has_fpa_or_afpa(float_hardware) || fpa_hasreg(fp_reg)) {
		register int status = fpa_rdscx();

		DEBUGF((fpadebug&SHOW_STATUS) || level,
			printf("fpa_status=0x%b tft=%s\n", status, "\20\4UNDER-ENABLE\5UNDERFLOW\6INEXACT-ENABLE\7INEXACT\10ROUND0\11ROUND1\12COND-Z\13COND-N\14PCK-DISABLE\15TASK-EXCEPTION\16BUS-EXCEPTION\17ABORTED\20PZERO",fpa_tft[status&0x07]));
		if (level++)
			panic("recursive FPA exception");
		fpa_status = status;		/* remember the status */
/*
 * test to see if we had a page fault on a dma operation
 */
		if (((status & FPA_TASK_EXCEPTION) == 0) &&
		    (GETSUBSEG(info) == GETSUBSEG(FPA_DMA_BASE)) &&
		    (float_has_afpa(float_hardware)
				|| float_has_afpa(u.u_floatmask))) {
			int i;
			int cps_status = ior(IOIM1+IOIM_CPS);
			int addr = (locr0[EX1_DATA] & ~0xff)
						| ((cps_status>>8) &0xfc);
			if (addr < locr0[EX1_DATA]) {
				addr += 0x100;		/* carry */
			}
			if (cps_status&0x100) {
				addr -= 4;	/* address was ahead by 4 */
			}
			DEBUGF(fpadebug&SHOW_PCI, printf("dma_addr=%x ", addr));

			/*
			 * Need to decrement level here, since pagein may sleep!
			 */
			--level;

			if (vtop(addr) < 0) {	/* if address could cause page fault */
				int error_save = u.u_error;

				if (pagein(addr, 0)) {
				/* not valid fault - check if stack growth needed */
					DEBUGF(fpadebug&SHOW_PCI,printf("pagein failed! "));
					if (GROWSP(locr0[SP]))
						i = 0;		/* stack grew */
					else
						i = SIGSEGV;
				u.u_error = error_save;
				} else
					i = 0;	/* was a handled page fault */
			} else
				i = SIGSEGV;
			DEBUGF(fpadebug&SHOW_PCI,printf("signal=%d\n",i));
			if (i)
				psignal(p, i);		/* Signal the user */
			return 1;
		}
		if ((status & FPA_TASK_EXCEPTION) == 0) {
			--level;
			return(0);		/* not fpa related */
		}
		++fpa_count[FPA_COUNT];		/* count fpa exceptions */

		if (!fpa_hasreg(fp_reg)) {
			++fpa_count[FPA_ALLOC];	/* count fpa exceptions */
			fpa_alloc();	/* get a register set */
			if ((p->p_flag&SOWEFPA) == 0) {
				--level;
				return(1);	/* exception handled */
			}
		}
		if (p->p_flag&SOWEFPA) {
			status = u.u_pcb.pcb_fpastatus;	/* remembered status */
			DEBUGF(fpadebug&(SHOW_STATUS|SHOW_DELAY),
				printf("delayed fpa_status=0x%b tft=%s\n", status, "\20\4UNDER-ENABLE\5UNDERFLOW\6INEXACT-ENABLE\7INEXACT\10ROUND0\11ROUND1\12COND-Z\13COND-N\14PCK-DISABLE\15TASK-EXCEPTION\16BUS-EXCEPTION\17ABORTED\20PZERO",fpa_tft[status&0x07]));
			p->p_flag &= ~SOWEFPA;	/* no longer owed to us */
		}
		fpa_status = status;		/* remember the status */

/*
 * if fpa exception happened during critical code in resume then remember
 * the exception and process it later 
 * by saving the register set as if it was grabbed by another process.
 * we then release the register set (though we don't actually lock
 * the fpa as that will be done by the resume code, which will also
 * set fpa_curreg).
 */
		if ((short *) (locr0[IAR]) == &resume_rdstr ||
				(short *) (locr0[IAR]) == &resume_rdstr + 1 ) {
			struct fpa_state *newsavep =
				    (struct fpa_state *) &floatsave[p-proc];

			++fpa_count[FPA_RESUME];	/* count exceptions */
				    /* Save fpa state in new area */
			fpa_save(newsavep, p, 0);
			p->p_flag |= SOWEFPA;	/* owed an fpa exception */
			p->p_fpareg = FPA_SAVED;	/* we saved it */
			FPA_CUR_UNUSED();
			REG_SET_FREE(fp_reg);		/* release registers */
			fpa_proc[fp_reg] = 0;		/* forget it */
			u.u_pcb.pcb_fpastatus = status;
			DEBUGF(fpadebug&(SHOW_PCI|SHOW_DELAY), printf("delaying FPA exception in resume\n"));
			--level;
			return(1);
		}
/*
 * test to see if the problem was an invalid reference to segment F
 * (indicated by a either no-exception (shouldn't happen) or
 * an illegal command in the status register); return to trap which
 * will deliver a SIGBUS to the process.
 */
		DEBUGF(fpadebug&SHOW_PCI,
			printf("fpa_status=0x%x\n", status));
		if ((status&07) == 7) {
			--level;
			return(0);	/* illegal command - give SIGBUS */
		}

/* Call IEEE fixup routine for arithmetic fixups*/
		/*
		 * Mask software level intr to get around the optimization
		 * bug that softclock() is being called directly from
		 * hardclock(), thus fp regs are not properly saved/restored.
		 * softclock() should be called from slih6().
		 */
		{
		int	s = spl1();
		fixup_rc = _fpfpx(status, u.u_floatmask);
		splx(s);
		}
		if (!fixup_rc) {
			DEBUGF(fpadebug&SHOW_PCI, printf("FPA/IEEE fixup done\n"));
			DEBUGF(fp_reg == FPA_SAVED && (fpadebug&(SHOW_STATUS|SHOW_DELAY)),
				{	/* display actual status */
				status = fpa_rdstr();
				printf("fpa_status=0x%b tft=%s\n", status, "\20\4UNDER-ENABLE\5UNDERFLOW\6INEXACT-ENABLE\7INEXACT\10ROUND0\11ROUND1\12COND-Z\13COND-N\14PCK-DISABLE\15TASK-EXCEPTION\16BUS-EXCEPTION\17ABORTED\20PZERO",fpa_tft[status&0x07]); } );
			++fpa_count[FPA_FIXED];			/* count fpa exceptions fixed */
			--level;
			return(1); 		/* handled */
		}
		--level;			/* copyout may sleep; psignal may exit! */
		copyout((caddr_t)(&machine),(caddr_t)USER_FPM,sizeof(FP_MACH));
		u.u_code = status;		/* pass to handler */
		++fpa_count[FPA_SIGFPE];	/* count fpa exceptions not fixed */
		psignal(p, SIGFPE);		/* signal FPE */
		return(1);			/* now handled */
	}

#define FPaGET0_DIRECT 0xcd230000	/* L r2,0(r2) */
#define FPaPUT0_DIRECT 0xdd230000	/* ST r2,0(r2) */
/*
 * check to see if this is a program compiled for direct mode running
 * without an FPA.
 * we assume this is so if the instruction is the correct format 
 * and the value in r3 has 0xff in the high-order byte.
 * in this case we arrange for control to be transfered to the floating
 * point emulator just as if it was called from user mode in compatible
 * mode.
 * we do this by setting the iar to the appropriate fpget or fpput
 * and r15 to be the (return) address following the offending instruction.
 */
	{
		register int iar = locr0[IAR];
		extern fpget, fpput;
		register u_short x;

		if (!isitok(iar, sizeof (int), KREAD_OK))
			return(0);		/* IAR is invalid */
		x = * (u_short *) iar;
		if ((x == ((unsigned) FPaGET0_DIRECT >> 16) || 
				x == ((unsigned) FPaPUT0_DIRECT >> 16)) &&
				(unsigned) locr0[R3] >> 24 == 0xff &&
				* (u_short *) (iar+2) == (FPaGET0_DIRECT & 0xffff)) {
			
			DEBUGF(nfldebug&0x02,
				prstate("fpa direct simulation", mcs_pcs, info, locr0[IAR], locr0[ICSCS],locr0));
			locr0[R15] = iar+4;		/* return address */
			locr0[IAR] = (x == (FPaGET0_DIRECT >> 16)) ?
				(int) &fpget : (unsigned) &fpput;	/* code address */
			return(1);			/* handled! */
		}
	}
	return(0);		/* not FPA related */
}

/*
 * utility routine to set floating point register contents
 * (invoked from ptrace)
 * return:	0	ok
 *		~0	failed
 */

fpa_putreg(reg,value)
u_int reg;
int *value;
{
	struct proc *p = u.u_procp;
	struct fpa_state *newsavep = (struct fpa_state *)&floatsave[p-proc];

	fpa_save(newsavep, p, 1);

	if (fpa_hasreg(p->p_fpareg)) {	/* has register set */
		REG_SET_FREE(p->p_fpareg);
		fpa_lockfp();			/* Lock the FPA */
		p->p_fpareg = FPA_SAVED;	/* mark as saved */
		FPA_CUR_UNUSED();
	}
	if ((reg < fpa_num_regs) ||
		(float_has_fpa(u.u_floatmask) && (reg < NUM_FPA_FLOATS+2))) {
		newsavep->programmer.data[reg] = *value;
		return 0;		/* succeeded */
	} else {
			    /* invalid register number */
		if (float_has_fpa(u.u_floatmask)) {
			return NUM_FPA_FLOATS+1;
		} else {
			return fpa_num_regs;
		}
	}
}


/*
 * We are in the process of sending a floating point signal
 * to a user process.  Kindly place information on the stack
 * to help the user's floating point signal handler.
 */

int
fpa_sendsig(floatsave)
struct floatsave *floatsave;
{
    return emul_sendsig(floatsave);
}

/*
 * Alter floating point registers 
 * (should be available to signal handler in the sigcontext structure;
 * an "FP_MACH" (defined in ieeetrap.h) which user can modify and
 * results get put back into emulator machine (if no FPA) or back
 * on card (if FPA)).
 */
fpa_sigcleanup(sf_scp)
struct sigcontext *sf_scp;
{
	FP_MACH usermach;
	int destreg;
	int update_status, result_size;

		/* Call to emulator to handle the updating of USER_FPM */
	emul_handle_cleanup(sf_scp, &destreg, &result_size, &update_status);

	copyin((caddr_t)USER_FPM,(caddr_t)(&usermach),sizeof(FP_MACH));

	/* 
	 * NOTE:  I am not sure that we really want to update the status
	 * register with that returned by the signal handler.  User can
	 * easily screw it up, and if he/she uses the swapfptrap function
	 * from the signal handler to do what seems reasonable, this
	 * would tromp on that one...
	 */

	switch (result_size) {
	case 0:
		break;		/* Don't update result */
	case sizeof (FLOAT):
		_FPAws(destreg, usermach.dreg[destreg/2].dfracth);
		break;
	case sizeof (DOUBLE):
		_FPAwl(destreg, usermach.dreg[destreg/2]);
		break;
	default:
		panic("fpa_sigcleanup: unknown result size.\n");
		/*NOTREACHED*/
	}

	if (update_status) {
		_FPAputs(usermach.status);
	}
}

/*
 * So, this works like this.  If the user wants to get rid of their
 * own register set, fine.  If the user wants to get a brand new
 * register set, no way (they have to go touch the hardware).
 *
 * If the superuser (though WE don't check) wants to change the
 * afpa:
 *	o	If they turn off AFPA_CONTROL_STORE_ENABLE, we disable
 *		the control store, etc. (via afpa_disable_control()).
 *	o	If they turn on AFPA_CONTROL_STORE_ENABLE, we
 *		enable the control store.
 *	o	If they go from !AFPA to AFPA, then we set up the
 *		various variables to say the afpa is present.
 */

void
fpa_setfloatstate(mask, state, newhardware, newuser)
struct floatstate *mask, *state;
int	newhardware,		/* What the new hardware will be */
	newuser;		/* What the new user will be */
{
    struct proc *p = u.u_procp;
		/*
		 * First, (silently) disallow changing either fpa state
		 * or the existence of the afpa hardware.
		 */
    mask->hardware_state &= ~(FLOAT_FPA|FLOAT_AFPA_HARDWARE|FLOAT_AFPA_DMA|FLOAT_AFPA_PASS_D);

    DEBUGF(fpadebug&SHOW_INIT, printf("fpa_setfloatstate: float_hardware=%b mask->hardware_state=%b newhardware=%b\n",float_hardware,FLOAT_FMT,mask->hardware_state,FLOAT_FMT,newhardware,FLOAT_FMT));


    /* If new configuration is AFPA_CONTROL_STORE_ENABLE and AFPA, complain */
    if ((newhardware&(FLOAT_AFPA_CONTROL_STORE_ENABLE|FLOAT_AFPA)) ==
				(FLOAT_AFPA_CONTROL_STORE_ENABLE|FLOAT_AFPA)) {
	log(LOG_CRIT,  "fpasetfloatstate: Illegal state \n");
	log(LOG_CRIT, "\t(FLOAT_AFPA and FLOAT_AFPA_CONTROL_STORE_ENABLE).\n");
	mask->hardware_state &= ~(FLOAT_AFPA|FLOAT_AFPA_CONTROL_STORE_ENABLE);
	newhardware = float_hardware;
    }

    /* Only allow one state to change at a time */

    if ((mask->hardware_state&FLOAT_AFPA) &&
		(mask->hardware_state&FLOAT_AFPA_CONTROL_STORE_ENABLE)) {
	log(LOG_CRIT, "fpasetfloatstate: Attempt to disable control store and\n");
	log(LOG_CRIT, "\tbring afpa online simultaneously not allowed by kernel.\n");
	mask->hardware_state &= ~(FLOAT_AFPA|FLOAT_AFPA_CONTROL_STORE_ENABLE);
	newhardware = float_hardware;
    }

    /*
     * Changing Control Store Enable
     */

    if ((newhardware&FLOAT_AFPA_CONTROL_STORE_ENABLE)
			!= (float_hardware&FLOAT_AFPA_CONTROL_STORE_ENABLE)) {
	if (newhardware&FLOAT_AFPA_CONTROL_STORE_ENABLE) {	/* enable */
	    if (afpa_enable_control() == 0) {
		mask->hardware_state &= ~FLOAT_AFPA_CONTROL_STORE_ENABLE;
		newhardware = float_hardware;
	    }
	} else {				/* Turning "down" off */
	    if (afpa_disable_control() == 0) {
		mask->hardware_state &= ~FLOAT_AFPA_CONTROL_STORE_ENABLE;
		newhardware = float_hardware;
	    }
	}
    } else if ((newhardware&FLOAT_AFPA) != (float_hardware&FLOAT_AFPA)) {
	if (newhardware&FLOAT_AFPA) {	/* Wants to turn on afpa */
	    if (((unsigned)state->fpa_registerset) > fpa_num_sets) {
		u.u_error = EIO;
		mask->hardware_state &= ~FLOAT_AFPA;
		newhardware = float_hardware;
	    } else {			/* Turn it online if possible */
		fpa_num_sets = state->fpa_registerset;
		if (afpa_online() == 0) {	/* failed */
			mask->hardware_state &= ~FLOAT_AFPA;
			newhardware = float_hardware;
	    	} else {			/* succeeded */
#ifdef ROMPC
			/*
			 * if we have just enabled AFPA, then enable AFPA_DMA if
			 * we are on a CPU that supports that feature.
			 */
#ifdef SGP
#ifdef MOD135
			if ((cpu_model == CPU_ROMPC) || (cpu_model == CPU_MOD135))
#else
			if (cpu_model == CPU_ROMPC)
#endif MOD135
#endif SGP
			{
			    mask->hardware_state |= FLOAT_AFPA_DMA;	/* we have DMA if ROMPC */
			    state->hardware_state |= FLOAT_AFPA_DMA;
			}
#endif ROMPC
		}
	    }
	} else {	/* We don't support turning off afpa */
	    mask->hardware_state &= ~FLOAT_AFPA;
	}
    }

    /*
     * Enabling/Disabling the AFPA or FPA for this process.
     */

    if ((mask->process_state&(FLOAT_FPA|FLOAT_AFPA)) &&
		(float_has_fpa_or_afpa(u.u_floatmask^state->process_state))) {
	/* The user is changing state.  Handle this. */

	if (float_has_fpa_or_afpa(state->process_state)) {
	    /* If they want it, let them touch it (then fpa_pci) */
	    mask->process_state &= ~(FLOAT_FPA|FLOAT_AFPA);
	} else {			/* They want to let the fpa go */
	    fpa_exit(p);
	}
    }

    /*
     * 	Changing register sets
     */

    if (mask->fpa_registerset) {
	if (suser()) {
	    int i;

	    DEBUGF(fpadebug&SHOW_INIT, printf("fpa_setfloatstate: registerset=%d\n",state->fpa_registerset));
	    if (fpa_hasreg(p->p_fpareg)) {
		fpa_save(
		    (struct fpa_state *)floatsave[p-proc].fpa_intreg, p, 0);
		REG_SET_FREE(p->p_fpareg);
		p->p_fpareg = FPA_SAVED;	/* mark as saved */
		FPA_CUR_UNUSED();
	    }
	    i = state->fpa_registerset;
	    if ((p->p_fpareg == FPA_NONE) &&
		((float_hardware&
			(FLOAT_AFPA_HARDWARE|FLOAT_AFPA_CONTROL_STORE_ENABLE))
			!= FLOAT_AFPA_HARDWARE)) {
		u.u_error = EIO;
		mask->fpa_registerset = 0;
	    } else if ((i < 0) || (i >= fpa_num_sets)) {
		u.u_error = EIO;
		mask->fpa_registerset = 0;
	    } else {
		if (REG_SET_IN_USE(i)) {
		    fpa_bump(i);		/* Free the register set */
		}
		fpa_register_set(i);		/* Get new set */
		u.u_floatmask |= FLOAT_AFPA;	/* Mark us using AFPA_FLOAT */
	    }
	} else {
	    /* EPERM already set */
	    mask->fpa_registerset = 0;
	}
    }
}
#endif	/* defined(FPA) */
