code:
/*	$OpenBSD: zs.c,v 1.57 2015/03/18 19:49:14 miod Exp $	*/
/*	$NetBSD: zs.c,v 1.50 1997/10/18 00:00:40 gwr Exp $	*/

/*-
 * Copyright (c) 1996 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Gordon W. Ross.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Zilog Z8530 Dual UART driver (machine-dependent part)
 *
 * Runs two serial lines per chip using slave drivers.
 * Plain tty/async lines use the zstty slave.
 * Sun keyboard/mouse uses the zskbd/zsms slaves.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/time.h>
#include <sys/syslog.h>

#include <machine/autoconf.h>
#include <machine/bsd_openprom.h>
#include <machine/conf.h>
#include <machine/cpu.h>
#include <machine/eeprom.h>
#if defined(SUN4)
#include <machine/oldmon.h>
#endif
#include <machine/psl.h>
#include <machine/z8530var.h>

#include <dev/cons.h>
#include <dev/ic/z8530reg.h>

#include <sparc/sparc/vaddrs.h>
#include <sparc/sparc/auxioreg.h>
#include <sparc/dev/cons.h>

#ifdef solbourne
#include <machine/prom.h>
#endif

#include <uvm/uvm_extern.h>

#include "zskbd.h"
#include "zs.h"

/* Make life easier for the initialized arrays here. */
#if NZS < 3
#undef  NZS
#define NZS 3
#endif

/*
 * Some warts needed by z8530tty.c -
 * The default parity REALLY needs to be the same as the PROM uses,
 * or you can not see messages done with printf during boot-up...
 */
int zs_def_cflag = (CREAD | CS8 | HUPCL);
int zs_major = 12;

/*
 * The Sun provides a 4.9152 MHz clock to the ZS chips.
 */
#define PCLK	(9600 * 512)	/* PCLK pin input clock rate */

#define	ZS_DELAY()		(CPU_ISSUN4C ? (0) : delay(2))

/* The layout of this is hardware-dependent (padding, order). */
struct zschan {
#if !defined(solbourne)
	volatile u_char	zc_csr;		/* ctrl,status, and indirect access */
	u_char		zc_xxx0;
	volatile u_char	zc_data;	/* data */
	u_char		zc_xxx1;
#endif
#if defined(solbourne)
	volatile u_char	zc_csr;		/* ctrl,status, and indirect access */
	u_char		zc_xxx0[7];
	volatile u_char	zc_data;	/* data */
	u_char		zc_xxx1[7];
#endif
};
struct zsdevice {
	/* Yes, they are backwards. */
	struct	zschan zs_chan_b;
	struct	zschan zs_chan_a;
};

/* Saved PROM mappings */
struct zsdevice *zsaddr[NZS];

/* Flags from cninit() */
int zs_hwflags[NZS][2];

/* Default speed for each channel */
int zs_defspeed[NZS][2] = {
	{ 9600, 	/* ttya */
	  9600 },	/* ttyb */
	{ 1200, 	/* keyboard */
	  1200 },	/* mouse */
	{ 9600, 	/* ttyc */
	  9600 },	/* ttyd */
};

u_char zs_init_reg[16] = {
	0,	/* 0: CMD (reset, etc.) */
	0,	/* 1: No interrupts yet. */
	0,	/* 2: IVECT */
	ZSWR3_RX_8 | ZSWR3_RX_ENABLE,
	ZSWR4_CLK_X16 | ZSWR4_ONESB,
	ZSWR5_TX_8 | ZSWR5_TX_ENABLE,
	0,	/* 6: TXSYNC/SYNCLO */
	0,	/* 7: RXSYNC/SYNCHI */
	0,	/* 8: alias for data port */
	ZSWR9_MASTER_IE | ZSWR9_NO_VECTOR,
	0,	/*10: Misc. TX/RX control bits */
	ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD,
	((PCLK/32)/9600)-2,	/*12: BAUDLO (default=9600) */
	0,			/*13: BAUDHI (default=9600) */
	ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK,
	ZSWR15_BREAK_IE /* | ZSWR15_DCD_IE */,
};

struct zschan *
zs_get_chan_addr(zs_unit, channel)
	int zs_unit, channel;
{
	struct zsdevice *addr;
	struct zschan *zc;

	if (zs_unit >= NZS)
		return NULL;
	addr = zsaddr[zs_unit];
	if (addr == NULL)
		addr = zsaddr[zs_unit] = findzs(zs_unit);
	if (addr == NULL)
		return NULL;
	if (channel == 0) {
		zc = &addr->zs_chan_a;
	} else {
		zc = &addr->zs_chan_b;
	}
	return (zc);
}

/****************************************************************
 * Autoconfig
 ****************************************************************/

/* Definition of the driver for autoconfig. */
int	zs_match(struct device *, void *, void *);
void	zs_attach(struct device *, struct device *, void *);
int	zs_print(void *, const char *nam);

/* Power management hooks (for Tadpole SPARCbooks) */
void	zs_disable(struct zs_chanstate *);
int	zs_enable(struct zs_chanstate *);

struct cfattach zs_ca = {
	sizeof(struct zsc_softc), zs_match, zs_attach
};

struct cfdriver zs_cd = {
	NULL, "zs", DV_DULL
};

/* Interrupt handlers. */
int zshard(void *);
void zssoft(void *);
struct intrhand levelhard = { zshard };

int zs_get_speed(struct zs_chanstate *);

/*
 * Is the zs chip present?
 */
int
zs_match(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct cfdata *cf = (struct cfdata *)vcf;
	struct confargs *ca = (struct confargs *)aux;
	struct romaux *ra = &ca->ca_ra;

	if (strcmp(cf->cf_driver->cd_name, ra->ra_name))
		return (0);

#ifdef solbourne
	if (CPU_ISKAP)
		return (ca->ca_bustype == BUS_OBIO);
#endif

	if ((ca->ca_bustype == BUS_MAIN && (CPU_ISSUN4C || CPU_ISSUN4E)) ||
	    (ca->ca_bustype == BUS_OBIO && CPU_ISSUN4M))
		return (getpropint(ra->ra_node, "slave", -2) == cf->cf_unit);
	ra->ra_len = NBPG;
	return (probeget(ra->ra_vaddr, 1) != -1);
}

/*
 * Attach a found zs.
 *
 * USE ROM PROPERTY keyboard FOR KEYBOARD/MOUSE?
 */
void
zs_attach(parent, self, aux)
	struct device *parent;
	struct device *self;
	void *aux;
{
	struct zsc_softc *zsc = (void *) self;
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;
	struct zsc_attach_args zsc_args;
	volatile struct zschan *zc;
	struct zs_chanstate *cs;
	int pri, s, zs_unit, channel;
	static int didintr, prevpri;

	zs_unit = zsc->zsc_dev.dv_unit;

	/* Use the mapping setup by the Sun PROM. */
	if (zsaddr[zs_unit] == NULL)
		zsaddr[zs_unit] = findzs(zs_unit);

	if (ca->ca_bustype==BUS_MAIN)
		if ((void*)zsaddr[zs_unit] != ra->ra_vaddr)
			panic("zsattach");
	if (ra->ra_nintr != 1) {
		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
		return;
	}
	pri = ra->ra_intr[0].int_pri;
	printf(" pri %d, softpri %d\n", pri, IPL_TTY);

	/*
	 * Initialize software state for each channel.
	 */
	for (channel = 0; channel < 2; channel++) {
		zsc_args.type = "serial";
		/* XXX hardcoded */
		if (zs_unit == 1) {
			if (channel == 0)
				zsc_args.type = "keyboard";
			if (channel == 1)
				zsc_args.type = "mouse";
		}

		zsc_args.channel = channel;
		zsc_args.hwflags = zs_hwflags[zs_unit][channel];

		if ((ca->ca_bustype == BUS_MAIN &&
		     (CPU_ISSUN4C || CPU_ISSUN4E)) ||
		    (ca->ca_bustype == BUS_OBIO && CPU_ISSUN4M)) {
			if (getproplen(ra->ra_node, channel == 0 ?
			    "port-a-ignore-cd" : "port-b-ignore-cd") == 0)
				zsc_args.hwflags |= ZS_HWFLAG_NO_DCD;
		}

		cs = zsc->zsc_cs[channel] =
			(struct zs_chanstate *)&zsc->zsc_cs_store[channel];

		cs->cs_channel = channel;
		cs->cs_private = NULL;
		cs->cs_ops = &zsops_null;
		cs->cs_brg_clk = PCLK / 16;
		zc = zs_get_chan_addr(zs_unit, channel);

		cs->cs_reg_csr  = &zc->zc_csr;
		cs->cs_reg_data = &zc->zc_data;
		bcopy(zs_init_reg, cs->cs_creg, 16);
		bcopy(zs_init_reg, cs->cs_preg, 16);

		/* XXX: Get these from the PROM properties! */
		/* XXX: See the mvme167 code.  Better. */
		if (zsc_args.hwflags & ZS_HWFLAG_CONSOLE)
			cs->cs_defspeed = zs_get_speed(cs);
		else
			cs->cs_defspeed = zs_defspeed[zs_unit][channel];
		cs->cs_defcflag = zs_def_cflag;

		/* Make these correspond to cs_defcflag (-crtscts) */
		cs->cs_rr0_dcd = ZSRR0_DCD;
		cs->cs_rr0_cts = 0;
		cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS;
		cs->cs_wr5_rts = 0;

		/*
		 * Clear the master interrupt enable.
		 * The INTENA is common to both channels,
		 * so just do it on the A channel.
		 */
		if (channel == 0) {
			zs_write_reg(cs, 9, 0);
		}

		/*
		 * Look for a child driver for this channel.
		 * The child attach will setup the hardware.
		 */
		if (!config_found(self, (void *)&zsc_args, zs_print)) {
			/* No sub-driver.  Just reset it. */
			u_char reset = (channel == 0) ?
				ZSWR9_A_RESET : ZSWR9_B_RESET;
			s = splzs();
			zs_write_reg(cs,  9, reset);
			splx(s);
		}
	}

	/*
	 * Now safe to install interrupt handlers.  Note the arguments
	 * to the interrupt handlers aren't used.  Note, we only do this
	 * once since both SCCs interrupt at the same level and vector.
	 */
	if (!didintr) {
		didintr = 1;
		prevpri = pri;
		intr_establish(pri, &levelhard, IPL_ZS, self->dv_xname);
	} else if (pri != prevpri)
		panic("broken zs interrupt scheme");

	zsc->zsc_softih = softintr_establish(IPL_SOFTTTY, zssoft, zsc);

	/*
	 * Set the master interrupt enable and interrupt vector.
	 * (common to both channels, do it on A)
	 */
	cs = zsc->zsc_cs[0];
	s = splhigh();
	/* interrupt vector */
	zs_write_reg(cs, 2, zs_init_reg[2]);
	/* master interrupt control (enable) */
	zs_write_reg(cs, 9, zs_init_reg[9]);
	splx(s);

#ifdef SUN4M
	/* register power management routines if necessary */
	if (CPU_ISSUN4M) {
		if (getpropint(ra->ra_node, "pwr-on-auxio2", 0))
			for (channel = 0; channel < 2; channel++) {
				cs = zsc->zsc_cs[channel];
				cs->disable = zs_disable;
				cs->enable = zs_enable;
				cs->enabled = 0;
			}
	}
#endif

#if 0
	/*
	 * XXX: L1A hack - We would like to be able to break into
	 * the debugger during the rest of autoconfiguration, so
	 * lower interrupts just enough to let zs interrupts in.
	 * This is done after both zs devices are attached.
	 */
	if (zs_unit == 1) {
		printf("zs1: enabling zs interrupts\n");
		(void)splfd(); /* XXX: splzs - 1 */
	}
#endif
}

int
zs_print(aux, name)
	void *aux;
	const char *name;
{
	struct zsc_attach_args *args = aux;

	if (name != NULL)
		printf("%s:", name);

	if (args->channel != -1)
		printf(" channel %d", args->channel);

	return UNCONF;
}

/*
 * Our ZS chips all share a common, autovectored interrupt,
 * so we have to look at all of them on each interrupt.
 */
int
zshard(arg)
	void *arg;
{
	struct zsc_softc *zsc;
	int unit, rr3, rval, softreq;

	rval = softreq = 0;
	for (unit = 0; unit < zs_cd.cd_ndevs; unit++) {
		zsc = zs_cd.cd_devs[unit];
		if (zsc == NULL)
			continue;
		rr3 = zsc_intr_hard(zsc);
		/* Count up the interrupts. */
		if (rr3) {
			rval |= rr3;
		}
		if (zsc->zsc_cs[0]->cs_softreq || zsc->zsc_cs[1]->cs_softreq)
			softintr_schedule(zsc->zsc_softih);
	}

	return (rval);
}

/*
 * Similar scheme as for zshard (look at all of them)
 */
void
zssoft(arg)
	void *arg;
{
	struct zsc_softc *zsc = (struct zsc_softc *)arg;
	int s;

	/* Make sure we call the tty layer at spltty. */
	s = spltty();
	(void)zsc_intr_soft(zsc);
	splx(s);
}

/*
 * Compute the current baud rate given a ZS channel.
 */
int
zs_get_speed(cs)
	struct zs_chanstate *cs;
{
	int tconst;

	tconst = zs_read_reg(cs, 12);
	tconst |= zs_read_reg(cs, 13) << 8;
	return (TCONST_TO_BPS(cs->cs_brg_clk, tconst));
}

/*
 * MD functions for setting the baud rate and control modes.
 */
int
zs_set_speed(cs, bps)
	struct zs_chanstate *cs;
	int bps;	/* bits per second */
{
	int tconst, real_bps;

	if (bps == 0)
		return (0);

#ifdef	DIAGNOSTIC
	if (cs->cs_brg_clk == 0)
		panic("zs_set_speed");
#endif

	tconst = BPS_TO_TCONST(cs->cs_brg_clk, bps);
	if (tconst < 0)
		return (EINVAL);

	/* Convert back to make sure we can do it. */
	real_bps = TCONST_TO_BPS(cs->cs_brg_clk, tconst);

	/* XXX - Allow some tolerance here? */
	if (real_bps != bps)
		return (EINVAL);

	cs->cs_preg[12] = tconst;
	cs->cs_preg[13] = tconst >> 8;

	/* Caller will stuff the pending registers. */
	return (0);
}

int
zs_set_modes(cs, cflag)
	struct zs_chanstate *cs;
	int cflag;
{
	int s;

	/*
	 * Output hardware flow control on the chip is horrendous:
	 * if carrier detect drops, the receiver is disabled, and if
	 * CTS drops, the transmitter is stopped IN MID CHARACTER!
	 * Therefore, NEVER set the HFC bit, and instead use the
	 * status interrupt to detect CTS changes.
	 */
	s = splzs();
	cs->cs_rr0_pps = 0;
	if ((cflag & (CLOCAL | MDMBUF)) != 0) {
		cs->cs_rr0_dcd = 0;
		if ((cflag & MDMBUF) == 0)
			cs->cs_rr0_pps = ZSRR0_DCD;
	} else
		cs->cs_rr0_dcd = ZSRR0_DCD;
	if ((cflag & CRTSCTS) != 0) {
		cs->cs_wr5_dtr = ZSWR5_DTR;
		cs->cs_wr5_rts = ZSWR5_RTS;
		cs->cs_rr0_cts = ZSRR0_CTS;
#if 0 /* JLW */
	} else if ((cflag & CDTRCTS) != 0) {
		cs->cs_wr5_dtr = 0;
		cs->cs_wr5_rts = ZSWR5_DTR;
		cs->cs_rr0_cts = ZSRR0_CTS;
#endif
	} else if ((cflag & MDMBUF) != 0) {
		cs->cs_wr5_dtr = 0;
		cs->cs_wr5_rts = ZSWR5_DTR;
		cs->cs_rr0_cts = ZSRR0_DCD;
	} else {
		cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS;
		cs->cs_wr5_rts = 0;
		cs->cs_rr0_cts = 0;
	}
	splx(s);

	/* Caller will stuff the pending registers. */
	return (0);
}

/*
 * Read or write the chip with suitable delays.
 */

u_char
zs_read_reg(cs, reg)
	struct zs_chanstate *cs;
	u_char reg;
{
	u_char val;

	*cs->cs_reg_csr = reg;
	ZS_DELAY();
	val = *cs->cs_reg_csr;
	ZS_DELAY();
	return val;
}

void
zs_write_reg(cs, reg, val)
	struct zs_chanstate *cs;
	u_char reg, val;
{
	*cs->cs_reg_csr = reg;
	ZS_DELAY();
	*cs->cs_reg_csr = val;
	ZS_DELAY();
}

u_char
zs_read_csr(cs)
	struct zs_chanstate *cs;
{
	register u_char val;

	val = *cs->cs_reg_csr;
	ZS_DELAY();
	return val;
}

void
zs_write_csr(cs, val)
	struct zs_chanstate *cs;
	u_char val;
{
	*cs->cs_reg_csr = val;
	ZS_DELAY();
}

u_char
zs_read_data(cs)
	struct zs_chanstate *cs;
{
	register u_char val;

	val = *cs->cs_reg_data;
	ZS_DELAY();
	return val;
}

void
zs_write_data(cs, val)
	struct zs_chanstate *cs;
	u_char val;
{
	*cs->cs_reg_data = val;
	ZS_DELAY();
}

#ifdef SUN4M
/*
 * Power management hooks for zsopen() and zsclose().
 * We use them to power on/off the ports on the Tadpole SPARCbook machines
 * (on other sun4m machines, this is a no-op).
 */

/*
 * Since the serial power control is global, we need to remember which channels
 * have their ports open, so as not to power off when closing one channel if
 * both were open. Simply xor'ing the zs_chanstate pointers is enough to let us
 * know if the serial lines are used or not.
 */
static vaddr_t zs_sb_enable = 0;

int
zs_enable(struct zs_chanstate *cs)
{
	if (cs->enabled == 0) {
		if (zs_sb_enable == 0)
			sb_auxregbisc(1, AUXIO2_SERIAL, 0);
		zs_sb_enable ^= (vaddr_t)cs;
		cs->enabled = 1;
	}
	return (0);
}

void
zs_disable(struct zs_chanstate *cs)
{
	if (cs->enabled != 0) {
		cs->enabled = 0;
		zs_sb_enable ^= (vaddr_t)cs;
		if (zs_sb_enable == 0)
			sb_auxregbisc(1, 0, AUXIO2_SERIAL);
	}
}

#endif	/* SUN4M */

/****************************************************************
 * Console support functions (Sun specific!)
 * Note: this code is allowed to know about the layout of
 * the chip registers, and uses that to keep things simple.
 * XXX - I think I like the mvme167 code better. -gwr
 ****************************************************************/

extern void Debugger(void);
void *zs_conschan;

/*
 * Handle user request to enter kernel debugger.
 */
void
zs_abort(cs)
	struct zs_chanstate *cs;
{
	volatile struct zschan *zc = zs_conschan;
	int rr0;

	/* Wait for end of break to avoid PROM abort. */
	/* XXX - Limit the wait? */
	do {
		rr0 = zc->zc_csr;
		ZS_DELAY();
	} while (rr0 & ZSRR0_BREAK);

#if defined(KGDB)
	zskgdb(cs);
#elif defined(DDB)
	{
		extern int db_active;

		if (!db_active)
			Debugger();
		else
			/* Debugger is probably hosed */
			callrom();
	}
#else
	printf("stopping on keyboard abort\n");
	callrom();
#endif
}

/*
 * Polled input char.
 */
int
zs_getc(arg)
	void *arg;
{
	volatile struct zschan *zc = arg;
	int s, c, rr0;

	s = splhigh();
	/* Wait for a character to arrive. */
	do {
		rr0 = zc->zc_csr;
		ZS_DELAY();
	} while ((rr0 & ZSRR0_RX_READY) == 0);

	c = zc->zc_data;
	ZS_DELAY();
	splx(s);

	return (c);
}

/*
 * Polled output char.
 */
void
zs_putc(arg, c)
	void *arg;
	int c;
{
	volatile struct zschan *zc = arg;
	int s, rr0;

	s = splhigh();
	/* Wait for transmitter to become ready. */
	do {
		rr0 = zc->zc_csr;
		ZS_DELAY();
	} while ((rr0 & ZSRR0_TX_READY) == 0);

	/*
	 * Send the next character.
	 * Now you'd think that this could be followed by a ZS_DELAY()
	 * just like all the other chip accesses, but it turns out that
	 * the `transmit-ready' interrupt isn't de-asserted until
	 * some period of time after the register write completes
	 * (more than a couple instructions).  So to avoid stray
	 * interrupts we put in the 2us delay regardless of cpu model.
	 */
        zc->zc_data = c;
	delay(2);

	splx(s);
}

/*****************************************************************/

cons_decl(zs);

/*
 * Console table shared by ttya, ttyb
 */
struct consdev zscn = {
	zscnprobe,
	zscninit,
	zscngetc,
	zscnputc,
	zscnpollc,
};

extern int zstty_unit;	/* set in consinit() */

void
zscnprobe(cn)
	struct consdev *cn;
{
	cn->cn_dev = makedev(zs_major, zstty_unit);
	cn->cn_pri = CN_HIGHPRI;
}

void
zscninit(cn)
	struct consdev *cn;
{
}

/*
 * Polled console input putchar.
 */
int
zscngetc(dev)
	dev_t dev;
{
	return (zs_getc(zs_conschan));
}

/*
 * Polled console output putchar.
 */
void
zscnputc(dev, c)
	dev_t dev;
	int c;
{
	zs_putc(zs_conschan, c);
}

int swallow_zsintrs;

void
zscnpollc(dev, on)
	dev_t dev;
	int on;
{
	/*
	 * Need to tell zs driver to acknowledge all interrupts or we get
	 * annoying spurious interrupt messages.  This is because mucking
	 * with spl() levels during polling does not prevent interrupts from
	 * being generated.
	 */

	if (on) swallow_zsintrs++;
	else swallow_zsintrs--;
}


code:
/*	$OpenBSD: zs_kgdb.c,v 1.6 2013/12/19 20:11:04 miod Exp $	*/
/*	$NetBSD: zs_kgdb.c,v 1.1 1997/10/18 00:00:51 gwr Exp $	*/

/*-
 * Copyright (c) 1996 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Gordon W. Ross.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Hooks for kgdb when attached via the z8530 driver
 *
 * To use this, build a kernel with: option KGDB, and
 * boot that kernel with "-d".  (The kernel will call
 * zs_kgdb_init, kgdb_connect.)  When the console prints
 * "kgdb waiting..." you run "gdb /path/to/bsd.gdb" on
 * the remote machine and do:
 *   (gdb) set remotebaud 19200
 *   (gdb) target remote /dev/ttyb
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/kgdb.h>

#include <dev/ic/z8530reg.h>
#include <machine/z8530var.h>
#include <sparc/dev/cons.h>

/* The Sun3 provides a 4.9152 MHz clock to the ZS chips. */
#define PCLK	(9600 * 512)	/* PCLK pin input clock rate */
#define ZSHARD_PRI	6	/* Wired on the CPU board... */

#define	ZS_DELAY()		(CPU_ISSUN4C ? (0) : delay(2))

/* The layout of this is hardware-dependent (padding, order). */
struct zschan {
	volatile u_char	zc_csr;		/* ctrl,status, and indirect access */
	u_char		zc_xxx0;
	volatile u_char	zc_data;	/* data */
	u_char		zc_xxx1;
};

void zs_setparam(struct zs_chanstate *, int, int);
struct zsops zsops_kgdb;

u_char zs_kgdb_regs[16] = {
	0,	/* 0: CMD (reset, etc.) */
	0,	/* 1: ~(ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE) */
	0,	/* 2: IVECT */
	ZSWR3_RX_8 | ZSWR3_RX_ENABLE,
	ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_EVENP,
	ZSWR5_TX_8 | ZSWR5_TX_ENABLE,
	0,	/* 6: TXSYNC/SYNCLO */
	0,	/* 7: RXSYNC/SYNCHI */
	0,	/* 8: alias for data port */
	ZSWR9_MASTER_IE | ZSWR9_NO_VECTOR,
	0,	/*10: Misc. TX/RX control bits */
	ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD,
	14,	/*12: BAUDLO (default=9600) */
	0,	/*13: BAUDHI (default=9600) */
	ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK,
	ZSWR15_BREAK_IE | ZSWR15_DCD_IE,
};

/*
 * This replaces "zs_reset()" in the sparc driver.
 */
void
zs_setparam(cs, iena, rate)
	struct zs_chanstate *cs;
	int iena;
	int rate;
{
	int s, tconst;

	bcopy(zs_kgdb_regs, cs->cs_preg, 16);

	if (iena) {
		cs->cs_preg[1] = ZSWR1_RIE | ZSWR1_SIE;
	}

	/* Initialize the speed, etc. */
	tconst = BPS_TO_TCONST(cs->cs_brg_clk, rate);
	cs->cs_preg[5] |= ZSWR5_DTR | ZSWR5_RTS;
	cs->cs_preg[12] = tconst;
	cs->cs_preg[13] = tconst >> 8;

	s = splhigh();
	zs_loadchannelregs(cs);
	splx(s);
}

/*
 * Set up for kgdb; called at boot time before configuration.
 * KGDB interrupts will be enabled later when zs0 is configured.
 * Called after cninit(), so printf() etc. works.
 */
void
zs_kgdb_init()
{
	struct zs_chanstate cs;
	volatile struct zschan *zc;
	int channel, zsc_unit;

	/* printf("zs_kgdb_init: kgdb_dev=0x%x\n", kgdb_dev); */
	if (major(kgdb_dev) != zs_major)
		return;

	/* Note: (ttya,ttyb) on zsc1, and (ttyc,ttyd) on zsc0 */
	zsc_unit = (kgdb_dev & 2) ? 0 : 1;
	channel  =  kgdb_dev & 1;
	printf("zs_kgdb_init: attaching tty%c at %d baud\n",
		   'a' + (kgdb_dev & 3), kgdb_rate);

	/* Setup temporary chanstate. */
	bzero((caddr_t)&cs, sizeof(cs));
	zc = zs_get_chan_addr(zsc_unit, channel);
	if (zc == NULL) {
		printf("zs_kgdb_init: zs not mapped.\n");
		kgdb_dev = -1;
		return;
	}

	cs.cs_channel = channel;
	cs.cs_brg_clk = PCLK / 16;
	cs.cs_reg_csr  = &zc->zc_csr;
	cs.cs_reg_data = &zc->zc_data;

	/* Now set parameters. (interrupts disabled) */
	zs_setparam(&cs, 0, kgdb_rate);

	/* Store the getc/putc functions and arg. */
	kgdb_attach(zs_getc, zs_putc, (void *)zc);
}

/*
 * This is a "hook" called by zstty_attach to allow the tty
 * to be "taken over" for exclusive use by kgdb.
 * Return non-zero if this is the kgdb port.
 *
 * Set the speed to kgdb_rate, CS8, etc.
 */
int
zs_check_kgdb(cs, dev)
	struct zs_chanstate *cs;
	int dev;
{

	if (dev != kgdb_dev)
		return (0);

	/*
	 * Yes, this is port in use by kgdb.
	 */
	cs->cs_private = NULL;
	cs->cs_ops = &zsops_kgdb;

	/* Now set parameters. (interrupts enabled) */
	zs_setparam(cs, 1, kgdb_rate);

	return (1);
}

/*
 * KGDB framing character received: enter kernel debugger.  This probably
 * should time out after a few seconds to avoid hanging on spurious input.
 */
void
zskgdb(cs)
	struct zs_chanstate *cs;
{
	int unit = minor(kgdb_dev);

	printf("zstty%d: kgdb interrupt\n", unit);
	/* This will trap into the debugger. */
	kgdb_connect(1);
}

/****************************************************************
 * Interface to the lower layer (zscc)
 ****************************************************************/

void zs_kgdb_rxint(struct zs_chanstate *);
void zs_kgdb_txint(struct zs_chanstate *);
void zs_kgdb_stint(struct zs_chanstate *, int);
void zs_kgdb_softint(struct zs_chanstate *);

int kgdb_input_lost;

void
zs_kgdb_rxint(cs)
	struct zs_chanstate *cs;
{
	register u_char c, rr1;

	/*
	 * First read the status, because reading the received char
	 * destroys the status of this char.
	 */
	rr1 = zs_read_reg(cs, 1);
	c = zs_read_data(cs);

	if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
		/* Clear the receive error. */
		zs_write_csr(cs, ZSWR0_RESET_ERRORS);
	}

	if (c == KGDB_START) {
		zskgdb(cs);
	} else {
		kgdb_input_lost++;
	}
}

void
zs_kgdb_txint(cs)
	register struct zs_chanstate *cs;
{
	register int rr0;

	rr0 = zs_read_csr(cs);
	zs_write_csr(cs, ZSWR0_RESET_TXINT);
}

void
zs_kgdb_stint(cs, force)
	register struct zs_chanstate *cs;
	int force;
{
	register int rr0;

	rr0 = zs_read_csr(cs);
	zs_write_csr(cs, ZSWR0_RESET_STATUS);

	/*
	 * Check here for console break, so that we can abort
	 * even when interrupts are locking up the machine.
	 */
	if (rr0 & ZSRR0_BREAK) {
		zskgdb(cs);
	}
}

void
zs_kgdb_softint(cs)
	struct zs_chanstate *cs;
{
	printf("zs_kgdb_softint?\n");
}

struct zsops zsops_kgdb = {
	zs_kgdb_rxint,	/* receive char available */
	zs_kgdb_stint,	/* external/status */
	zs_kgdb_txint,	/* xmit buffer empty */
	zs_kgdb_softint,	/* process software interrupt */
};

license: unknown


code:
/*	$OpenBSD: zx.c,v 1.27 2015/03/28 19:07:08 miod Exp $	*/
/*	$NetBSD: zx.c,v 1.5 2002/10/02 16:52:46 thorpej Exp $	*/

/*
 * Copyright (c) 2003, Miodrag Vallat.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Derived from NetBSD syssrc/sys/dev/sbus/zx.c under the following licence
 * terms:
 *
 *  Copyright (c) 2002 The NetBSD Foundation, Inc.
 *  All rights reserved.
 *
 *  This code is derived from software contributed to The NetBSD Foundation
 *  by Andrew Doran.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 *  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Driver for the Sun ZX display adapter.  This would be called 'leo', but
 * NetBSD/amiga already has a driver by that name.  The XFree86 and Linux
 * drivers were used as "living documentation" when writing this; thanks
 * to the authors.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/tty.h>
#include <sys/conf.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/conf.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>
#include <machine/fbvar.h>

#include <dev/sbus/zxreg.h>

#include <dev/cons.h>	/* for prom console hook */

#define	ZX_WID_SHARED_8		0
#define	ZX_WID_SHARED_24	1
#define	ZX_WID_DBL_8		2
#define	ZX_WID_DBL_24		3

/*
 * Per-instance data.
 */

struct zx_cmap {
	u_int8_t	cm_red[256];
	u_int8_t	cm_green[256];
	u_int8_t	cm_blue[256];
};

struct zx_softc {
	struct	sunfb	sc_sunfb;
	struct	rom_reg	sc_phys;

	struct	zx_cmap	sc_cmap;	/* shadow color map for overlay plane */

	volatile struct zx_command *sc_zc;
	volatile struct zx_cross *sc_zx;
	volatile struct zx_draw *sc_zd_ss0;
	volatile struct zx_draw_ss1 *sc_zd_ss1;
	volatile struct zx_cursor *sc_zcu;

	int	sc_mode;
	int	sc_isconsole;
};

void	zx_burner(void *, u_int, u_int);
int	zx_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t	zx_mmap(void *v, off_t offset, int prot);
void	zx_prom(struct zx_softc *);
void	zx_reset(struct zx_softc *, u_int);
void	zx_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);

struct wsdisplay_accessops zx_accessops = {
	.ioctl = zx_ioctl,
	.mmap = zx_mmap,
	.burn_screen = zx_burner
};

/* Force 32-bit writes. */
#define	SETREG(r, v)	(*((volatile u_int32_t *)&r) = (v))

#define	ZX_STD_ROP	(ZX_ROP_NEW | ZX_ATTR_WE_ENABLE | \
    ZX_ATTR_OE_ENABLE | ZX_ATTR_FORCE_WID)

#define	ZX_BWIDTH	13
#define	ZX_WWIDTH	11	/* word width */

#define	ZX_COORDS(x, y)	((x) | ((y) << ZX_WWIDTH))

void	zx_attach(struct device *, struct device *, void *);
int	zx_match(struct device *, void *, void *);
int	zx_activate(struct device *, int);

void	zx_copyrect(struct rasops_info *, int, int, int, int, int, int);
int	zx_cross_loadwid(struct zx_softc *, u_int, u_int, u_int);
int	zx_cross_wait(struct zx_softc *);
void	zx_fillrect(struct rasops_info *, int, int, int, int, long, int);
int	zx_intr(void *);
int	zx_putcmap(struct zx_softc *);

int	zx_copycols(void *, int, int, int, int);
int	zx_copyrows(void *, int, int, int);
int	zx_do_cursor(struct rasops_info *);
int	zx_erasecols(void *, int, int, int, long);
int	zx_eraserows(void *, int, int, long);
int	zx_putchar(void *, int, int, u_int, long);

struct cfattach zx_ca = {
	sizeof(struct zx_softc), zx_match, zx_attach,
	NULL, zx_activate
};

struct cfdriver zx_cd = {
	NULL, "zx", DV_DULL
};

int
zx_match(struct device *parent, void *vcf, void *aux)
{
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

	if (strcmp(ra->ra_name, "SUNW,leo") == 0)
		return (1);

	return (0);
}

void
zx_attach(struct device *parent, struct device *self, void *args)
{
	struct zx_softc *sc = (struct zx_softc *)self;
	struct confargs *ca = args;
	struct rasops_info *ri;
	int node;
	const char *nam;

	ri = &sc->sc_sunfb.sf_ro;
	node = ca->ca_ra.ra_node;

	/*
	 * Map the various parts of the card.
	 */
	sc->sc_phys = ca->ca_ra.ra_reg[0];

	sc->sc_zc = (struct zx_command *)
	    mapiodev(ca->ca_ra.ra_reg, ZX_OFF_LC_SS0_USR,
	        sizeof(struct zx_command));
	sc->sc_zd_ss0 = (struct zx_draw *)
	    mapiodev(ca->ca_ra.ra_reg, ZX_OFF_LD_SS0,
	        sizeof(struct zx_draw));
	sc->sc_zd_ss1 = (struct zx_draw_ss1 *)
	    mapiodev(ca->ca_ra.ra_reg, ZX_OFF_LD_SS1,
	        sizeof(struct zx_draw_ss1));
	sc->sc_zx = (struct zx_cross *)
	    mapiodev(ca->ca_ra.ra_reg, ZX_OFF_LX_CROSS,
	        sizeof(struct zx_cross));
	sc->sc_zcu = (struct zx_cursor *)
	    mapiodev(ca->ca_ra.ra_reg, ZX_OFF_LX_CURSOR,
	        sizeof(struct zx_cursor));

	nam = getpropstring(node, "model");
	if (*nam == '\0')
		nam = ca->ca_ra.ra_name;
	printf(": %s", nam);

	sc->sc_isconsole = node == fbnode;

	/*
	 * The console is using the 8-bit overlay plane, while the prom
	 * will correctly report 32 bit depth.
	 * The following is an equivalent for
	 *    fb_setsize(&sc->sc_sunfb, 8, 1152, 900, node, ca->ca_bustype);
	 * forcing the depth value not to be overwritten.
	 * Furthermore, the linebytes value is in fact 8192 bytes.
	 */
	sc->sc_sunfb.sf_depth = 8;
	sc->sc_sunfb.sf_width = getpropint(node, "width", 1152);
	sc->sc_sunfb.sf_height = getpropint(node, "height", 900);
	sc->sc_sunfb.sf_linebytes = 1 << ZX_BWIDTH;
	sc->sc_sunfb.sf_fbsize = sc->sc_sunfb.sf_height << ZX_BWIDTH;

	printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);

	ri->ri_bits = mapiodev(ca->ca_ra.ra_reg,
	    ZX_OFF_SS0, round_page(sc->sc_sunfb.sf_fbsize));
	ri->ri_hw = sc;

	fbwscons_init(&sc->sc_sunfb, sc->sc_isconsole);

	/*
	 * Watch out! rasops_init() invoked via fbwscons_init() did not
	 * compute ri_bits correctly when centering the display, because
	 * it has been tricked with the low depth value.
	 * Recompute now.
	 */
	ri->ri_emustride = ri->ri_emuwidth * 4;
	ri->ri_delta = ri->ri_stride - ri->ri_emustride;
	ri->ri_pelbytes = 4;
	ri->ri_xscale = ri->ri_font->fontwidth * 4;
	ri->ri_bits = ri->ri_origbits;
	ri->ri_bits += (((ri->ri_width * 4) - ri->ri_emustride) >> 1) & ~3;
	ri->ri_bits += ((ri->ri_height - ri->ri_emuheight) >> 1) *
	    ri->ri_stride;
	ri->ri_yorigin = (int)(ri->ri_bits - ri->ri_origbits)
	    / ri->ri_stride;
	ri->ri_xorigin = (((int)(ri->ri_bits - ri->ri_origbits)
	    % ri->ri_stride) / 4);

	ri->ri_ops.copyrows = zx_copyrows;
	ri->ri_ops.copycols = zx_copycols;
	ri->ri_ops.eraserows = zx_eraserows;
	ri->ri_ops.erasecols = zx_erasecols;
	ri->ri_ops.putchar = zx_putchar;
	ri->ri_do_cursor = zx_do_cursor;

	if (sc->sc_isconsole) {
		/* zx_reset() below will clear screen, so restart at 1st row */
		fbwscons_console_init(&sc->sc_sunfb, 0);
	}

	/* reset cursor & frame buffer controls */
	sc->sc_mode = ~WSDISPLAYIO_MODE_EMUL;	/* force action */
	zx_reset(sc, WSDISPLAYIO_MODE_EMUL);

	/* enable video */
	zx_burner(sc, 1, 0);

	fbwscons_attach(&sc->sc_sunfb, &zx_accessops, sc->sc_isconsole);
}

int
zx_activate(struct device *self, int act)
{
	struct zx_softc *sc = (struct zx_softc *)self;
	int ret = 0;

	switch (act) {
	case DVACT_POWERDOWN:
		if (sc->sc_isconsole)
			zx_prom(sc);
		break;
	}

	return (ret);
}

int
zx_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	struct zx_softc *sc = dev;
	struct wsdisplay_fbinfo *wdf;

	/*
	 * Note that, although the emulation (text) mode is running in
	 * a 8-bit plane, we advertize the frame buffer as the full-blown
	 * 32-bit beast it is.
	 */
	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_SUNLEO;
		break;
	case WSDISPLAYIO_GINFO:
		wdf = (struct wsdisplay_fbinfo *)data;
		wdf->height = sc->sc_sunfb.sf_height;
		wdf->width = sc->sc_sunfb.sf_width;
		wdf->depth = 32;
		wdf->cmsize = 0;
		break;
	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
		*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
		break;
	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
		break;

	case WSDISPLAYIO_GETCMAP:
	case WSDISPLAYIO_PUTCMAP:
		break;

	case WSDISPLAYIO_SMODE:
		zx_reset(sc, *(u_int *)data);
		break;

	case WSDISPLAYIO_SVIDEO:
	case WSDISPLAYIO_GVIDEO:
		break;

	default:
		return (-1);
	}

	return (0);
}

/*
 * Return the address that would map the given device at the given
 * offset, allowing for the given protection, or return -1 for error.
 */
paddr_t
zx_mmap(void *v, off_t offset, int prot)
{
	struct zx_softc *sc = v;

	if (offset & PGOFSET)
		return (-1);

	/* Allow mapping as a dumb framebuffer from offset 0 */
	if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize) {
		return (REG2PHYS(&sc->sc_phys, ZX_OFF_SS0 + offset) | PMAP_NC);
	}

	return (-1);
}

void
zx_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
	struct zx_softc *sc = v;

	sc->sc_cmap.cm_red[index] = r;
	sc->sc_cmap.cm_green[index] = g;
	sc->sc_cmap.cm_blue[index] = b;
}

void
zx_reset(struct zx_softc *sc, u_int mode)
{
	volatile struct zx_draw *zd;
	volatile struct zx_command *zc;
	u_int32_t i;
	const u_char *color;
	u_int8_t *r, *g, *b;

	if (mode == sc->sc_mode)
		return;

	zd = sc->sc_zd_ss0;
	zc = sc->sc_zc;

	if (mode == WSDISPLAYIO_MODE_EMUL) {
		/* Back from X11 to emulation mode, or first reset */
		zx_cross_loadwid(sc, ZX_WID_DBL_8, 0, 0x2c0);
		zx_cross_loadwid(sc, ZX_WID_DBL_8, 1, 0x30);
		zx_cross_loadwid(sc, ZX_WID_DBL_8, 2, 0x20);
		zx_cross_loadwid(sc, ZX_WID_DBL_24, 1, 0x30);

		i = sc->sc_zd_ss1->zd_misc;
		i |= ZX_SS1_MISC_ENABLE;
		SETREG(sc->sc_zd_ss1->zd_misc, i);

		/*
		 * XXX
		 * If zc_fill is not set to that value, there will be black
		 * bars left in the margins. But then with this value, the
		 * screen gets cleared. Go figure.
		 */
		SETREG(zd->zd_wid, 0xffffffff);
		SETREG(zd->zd_wmask, 0xffff);
		SETREG(zd->zd_vclipmin, 0);
		SETREG(zd->zd_vclipmax, (sc->sc_sunfb.sf_width - 1) |
		    ((sc->sc_sunfb.sf_height - 1) << 16));
		SETREG(zd->zd_fg, 0);
		SETREG(zd->zd_planemask, 0xff000000);
		SETREG(zd->zd_rop, ZX_STD_ROP);
		SETREG(zd->zd_widclip, 0);

		SETREG(zc->zc_extent, ZX_COORDS(sc->sc_sunfb.sf_width - 1,
		    sc->sc_sunfb.sf_height - 1));
		SETREG(zc->zc_addrspace, ZX_ADDRSPC_FONT_OBGR);
		SETREG(zc->zc_fill, ZX_COORDS(0, 0) | ZX_EXTENT_DIR_BACKWARDS);
		SETREG(zc->zc_fontt, 0);

		while ((zc->zc_csr & ZX_CSR_BLT_BUSY) != 0)
			;

		/*
		 * Initialize the 8-bit colormap
		 */
		r = sc->sc_cmap.cm_red;
		g = sc->sc_cmap.cm_green;
		b = sc->sc_cmap.cm_blue;
		color = rasops_cmap;
		for (i = 0; i < 256; i++) {
			*r++ = *color++;
			*g++ = *color++;
			*b++ = *color++;
		}
		fbwscons_setcolormap(&sc->sc_sunfb, zx_setcolor);
		zx_putcmap(sc);
	} else {
		/* Starting X11 - switch to 24bit WID */
		SETREG(zd->zd_wid, 1);
		SETREG(zd->zd_widclip, 0);
		SETREG(zd->zd_wmask, 0xffff);
		SETREG(zd->zd_planemask, 0x00ffffff);
		SETREG(zc->zc_extent, ZX_COORDS(sc->sc_sunfb.sf_width - 1,
		    sc->sc_sunfb.sf_height - 1));
		SETREG(zc->zc_fill, 0);
		while ((zc->zc_csr & ZX_CSR_BLT_BUSY) != 0)
			;

		SETREG(zc->zc_addrspace, ZX_ADDRSPC_OBGR);
		SETREG(zd->zd_rop, ZX_ATTR_RGBE_ENABLE |
		    ZX_ROP_NEW /* | ZX_ATTR_FORCE_WID */);
	}

	sc->sc_mode = mode;
}

int
zx_cross_wait(struct zx_softc *sc)
{
	volatile struct zx_cross *zx;
	int i;

	zx = sc->sc_zx;

	for (i = 300000; i != 0; i--) {
		if ((zx->zx_csr & ZX_CROSS_CSR_PROGRESS) == 0)
			break;
		DELAY(1);
	}

	if (i == 0)
		printf("%s: zx_cross_wait: timed out\n",
		    sc->sc_sunfb.sf_dev.dv_xname);

	return (i);
}

int
zx_cross_loadwid(struct zx_softc *sc, u_int type, u_int index, u_int value)
{
	volatile struct zx_cross *zx;
	u_int tmp;

	zx = sc->sc_zx;
	SETREG(zx->zx_type, ZX_CROSS_TYPE_WID);

	if (!zx_cross_wait(sc))
		return (1);

	if (type == ZX_WID_DBL_8)
		tmp = (index & 0x0f) + 0x40;
	else  /* ZX_WID_DBL_24 */
		tmp = index & 0x3f;

	SETREG(zx->zx_type, 0x5800 + tmp);
	SETREG(zx->zx_value, value);
	SETREG(zx->zx_type, ZX_CROSS_TYPE_WID);
	SETREG(zx->zx_csr, ZX_CROSS_CSR_UNK | ZX_CROSS_CSR_UNK2);

	return (0);
}

int
zx_putcmap(struct zx_softc *sc)
{
	volatile struct zx_cross *zx;
	u_int32_t i;
	u_int8_t *r, *g, *b;

	zx = sc->sc_zx;

	SETREG(zx->zx_type, ZX_CROSS_TYPE_CLUT0);
	if (!zx_cross_wait(sc))
		return (1);

	SETREG(zx->zx_type, ZX_CROSS_TYPE_CLUTDATA);

	r = sc->sc_cmap.cm_red;
	g = sc->sc_cmap.cm_green;
	b = sc->sc_cmap.cm_blue;
	for (i = 0; i < 256; i++) {
		SETREG(zx->zx_value, *r++ | (*g++ << 8) | (*b++ << 16));
	}

	SETREG(zx->zx_type, ZX_CROSS_TYPE_CLUT0);
	i = zx->zx_csr;
	i = i | ZX_CROSS_CSR_UNK | ZX_CROSS_CSR_UNK2;
	SETREG(zx->zx_csr, i);
	return (0);
}

void
zx_burner(void *v, u_int on, u_int flags)
{
	struct zx_softc *sc = v;
	volatile struct zx_cross *zx;
	u_int32_t i;

	zx = sc->sc_zx;

	SETREG(zx->zx_type, ZX_CROSS_TYPE_VIDEO);
	i = zx->zx_csr;
	if (on) {
		i |= ZX_CROSS_CSR_ENABLE;
	} else {
		i &= ~ZX_CROSS_CSR_ENABLE;
	}
	SETREG(zx->zx_csr, i);
}

void
zx_fillrect(struct rasops_info *ri, int x, int y, int w, int h, long attr,
    int rop)
{
	struct zx_softc *sc;
	volatile struct zx_command *zc;
	volatile struct zx_draw *zd;
	int fg, bg;

	sc = ri->ri_hw;
	zc = sc->sc_zc;
	zd = sc->sc_zd_ss0;

	ri->ri_ops.unpack_attr(ri, attr, &fg, &bg, NULL);
	x = x * ri->ri_font->fontwidth + ri->ri_xorigin;
	y = y * ri->ri_font->fontheight + ri->ri_yorigin;
	w = ri->ri_font->fontwidth * w - 1;
	h = ri->ri_font->fontheight * h - 1;

	while ((zc->zc_csr & ZX_CSR_BLT_BUSY) != 0)
		;

	SETREG(zd->zd_rop, rop);
	SETREG(zd->zd_fg, ri->ri_devcmap[bg] << 24);
	SETREG(zc->zc_extent, ZX_COORDS(w, h));
	SETREG(zc->zc_fill, ZX_COORDS(x, y) | ZX_EXTENT_DIR_BACKWARDS);
}

void
zx_copyrect(struct rasops_info *ri, int sx, int sy, int dx, int dy, int w,
    int h)
{
	struct zx_softc *sc;
	volatile struct zx_command *zc;
	volatile struct zx_draw *zd;
	int dir;

	sc = ri->ri_hw;
	zc = sc->sc_zc;
	zd = sc->sc_zd_ss0;

	sx = sx * ri->ri_font->fontwidth + ri->ri_xorigin;
	sy = sy * ri->ri_font->fontheight + ri->ri_yorigin;
	dx = dx * ri->ri_font->fontwidth + ri->ri_xorigin;
	dy = dy * ri->ri_font->fontheight + ri->ri_yorigin;
	w = w * ri->ri_font->fontwidth - 1;
	h = h * ri->ri_font->fontheight - 1;

	if (sy < dy || sx < dx) {
		dir = ZX_EXTENT_DIR_BACKWARDS;
		sx += w;
		sy += h;
		dx += w;
		dy += h;
	} else
		dir = ZX_EXTENT_DIR_FORWARDS;

	while ((zc->zc_csr & ZX_CSR_BLT_BUSY) != 0)
		;

	SETREG(zd->zd_rop, ZX_STD_ROP);
	SETREG(zc->zc_extent, ZX_COORDS(w, h) | dir);
	SETREG(zc->zc_src, ZX_COORDS(sx, sy));
	SETREG(zc->zc_copy, ZX_COORDS(dx, dy));
}

int
zx_do_cursor(struct rasops_info *ri)
{

	zx_fillrect(ri, ri->ri_ccol, ri->ri_crow, 1, 1, WSCOL_BLACK << 16,
	    ZX_ROP_NEW_XOR_OLD | ZX_ATTR_WE_ENABLE | ZX_ATTR_OE_ENABLE |
	    ZX_ATTR_FORCE_WID);

	return 0;
}

int
zx_erasecols(void *cookie, int row, int col, int num, long attr)
{
	struct rasops_info *ri;

	ri = (struct rasops_info *)cookie;

	zx_fillrect(ri, col, row, num, 1, attr, ZX_STD_ROP);

	return 0;
}

int
zx_eraserows(void *cookie, int row, int num, long attr)
{
	struct rasops_info *ri;
	struct zx_softc *sc;
	volatile struct zx_command *zc;
	volatile struct zx_draw *zd;
	int fg, bg;

	ri = (struct rasops_info *)cookie;

	if (num == ri->ri_rows && (ri->ri_flg & RI_FULLCLEAR)) {
		sc = ri->ri_hw;
		zc = sc->sc_zc;
		zd = sc->sc_zd_ss0;

		ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);

		while ((zc->zc_csr & ZX_CSR_BLT_BUSY) != 0)
			;

		SETREG(zd->zd_rop, ZX_STD_ROP);
		SETREG(zd->zd_fg, ri->ri_devcmap[bg] << 24);
		SETREG(zc->zc_extent,
		    ZX_COORDS(ri->ri_width - 1, ri->ri_height - 1));
		SETREG(zc->zc_fill, ZX_COORDS(0, 0) | ZX_EXTENT_DIR_BACKWARDS);
	} else
		zx_fillrect(ri, 0, row, ri->ri_cols, num, attr, ZX_STD_ROP);

	return 0;
}

int
zx_copyrows(void *cookie, int src, int dst, int num)
{
	struct rasops_info *ri;

	ri = (struct rasops_info *)cookie;

	zx_copyrect(ri, 0, src, 0, dst, ri->ri_cols, num);

	return 0;
}

int
zx_copycols(void *cookie, int row, int src, int dst, int num)
{
	struct rasops_info *ri;

	ri = (struct rasops_info *)cookie;

	zx_copyrect(ri, src, row, dst, row, num, 1);

	return 0;
}

int
zx_putchar(void *cookie, int row, int col, u_int uc, long attr)
{
	struct rasops_info *ri;
	struct zx_softc *sc;
	struct wsdisplay_font *font;
	volatile struct zx_command *zc;
	volatile struct zx_draw *zd;
	volatile u_int32_t *dp;
	u_int8_t *fb;
	int fs, i, fg, bg, ul;

	ri = (struct rasops_info *)cookie;
	font = ri->ri_font;
	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, &ul);
	fg = ri->ri_devcmap[fg];
	bg = ri->ri_devcmap[bg];

	dp = (volatile u_int32_t *)ri->ri_bits +
	    ZX_COORDS(col * font->fontwidth, row * font->fontheight);

	if (uc == ' ') {
		zx_fillrect(ri, col, row, 1, 1, attr, ZX_STD_ROP);
		if (ul == 0)
			return 0;

		dp += font->fontheight << ZX_WWIDTH;

		while ((zc->zc_csr & ZX_CSR_BLT_BUSY) != 0)
			;

		SETREG(zd->zd_rop, ZX_STD_ROP);
		SETREG(zd->zd_fg, fg << 24);
		SETREG(zd->zd_bg, bg << 24);
		SETREG(zc->zc_fontmsk, 0xffffffff << (32 - font->fontwidth));
	} else {
		sc = ri->ri_hw;
		zc = sc->sc_zc;
		zd = sc->sc_zd_ss0;

		fb = (u_int8_t *)font->data + (uc - font->firstchar) *
		    ri->ri_fontscale;
		fs = font->stride;

		while ((zc->zc_csr & ZX_CSR_BLT_BUSY) != 0)
			;

		SETREG(zd->zd_rop, ZX_STD_ROP);
		SETREG(zd->zd_fg, fg << 24);
		SETREG(zd->zd_bg, bg << 24);
		SETREG(zc->zc_fontmsk, 0xffffffff << (32 - font->fontwidth));

		if (font->fontwidth <= 8) {
			for (i = font->fontheight; i != 0;
			    i--, dp += 1 << ZX_WWIDTH) {
				*dp = *fb << 24;
				fb += fs;
			}
		} else {
			for (i = font->fontheight; i != 0;
			    i--, dp += 1 << ZX_WWIDTH) {
				*dp = *((u_int16_t *)fb) << 16;
				fb += fs;
			}
		}
	}

	/* underline */
	if (ul) {
		dp -= 2 << ZX_WWIDTH;
		*dp = 0xffffffff;
	}

	return 0;
}

void
zx_prom(struct zx_softc *sc)
{
	extern struct consdev consdev_prom;

	if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL) {
		/*
	 	 * Select 8-bit mode.
	 	 */
		zx_reset(sc, WSDISPLAYIO_MODE_EMUL);

		/*
	 	 * Go back to prom output for the last few messages, so they
	 	 * will be displayed correctly.
	 	 */
		cn_tab = &consdev_prom;
	}
}


code: src/sys/arch/sparc/fpu

path: sys/arch/sparc/fpu/CVS/Repository


code:
/*	$OpenBSD: fpu.c,v 1.13 2013/11/26 20:33:14 deraadt Exp $	*/
/*	$NetBSD: fpu.c,v 1.6 1997/07/29 10:09:51 fair Exp $	*/

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fpu.c	8.1 (Berkeley) 6/11/93
 */

#include <sys/param.h>
#include <sys/proc.h>
#include <sys/signal.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/signalvar.h>

#include <machine/instr.h>
#include <machine/reg.h>

#include <sparc/fpu/fpu_emu.h>
#include <sparc/fpu/fpu_extern.h>

/*
 * fpu_execute returns the following error numbers (0 = no error):
 */
#define	FPE		1	/* take a floating point exception */
#define	NOTFPU		2	/* not an FPU instruction */

/*
 * Translate current exceptions into `first' exception.  The
 * bits go the wrong way for ffs() (0x10 is most important, etc).
 * There are only 5, so do it the obvious way.
 */
#define	X1(x) x
#define	X2(x) x,x
#define	X4(x) x,x,x,x
#define	X8(x) X4(x),X4(x)
#define	X16(x) X8(x),X8(x)

static char cx_to_trapx[] = {
	X1(FSR_NX),
	X2(FSR_DZ),
	X4(FSR_UF),
	X8(FSR_OF),
	X16(FSR_NV)
};
static u_char fpu_codes[] = {
	X1(FPE_FLTINEX_TRAP),
	X2(FPE_FLTDIV_TRAP),
	X4(FPE_FLTUND_TRAP),
	X8(FPE_FLTOVF_TRAP),
	X16(FPE_FLTOPERR_TRAP)
};

static int fpu_types[] = {
	X1(FPE_FLTRES),
	X2(FPE_FLTDIV),
	X4(FPE_FLTUND),
	X8(FPE_FLTOVF),
	X16(FPE_FLTINV)
};

/*
 * The FPU gave us an exception.  Clean up the mess.  Note that the
 * fp queue can only have FPops in it, never load/store FP registers
 * nor FBfcc instructions.  Experiments with `crashme' prove that
 * unknown FPops do enter the queue, however.
 */
void
fpu_cleanup(p, fs)
	register struct proc *p;
	register struct fpstate *fs;
{
	register int i, fsr = fs->fs_fsr, error;
	union instr instr;
	union sigval sv;
	struct fpemu fe;

	sv.sival_int = p->p_md.md_tf->tf_pc;  /* XXX only approximate */

	switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) {

	case FSR_TT_NONE:
#if 0
		/* XXX I'm not sure how we get here, but ignoring the trap */
		/* XXX seems to work in my limited tests		   */
		/* XXX More research to be done =)			   */
		panic("fpu_cleanup 1"); /* ??? */
#else
		printf("fpu_cleanup 1\n");
#endif
		break;

	case FSR_TT_IEEE:
		if ((i = fsr & FSR_CX) == 0)
			panic("fpu ieee trap, but no exception");
		trapsignal(p, SIGFPE, fpu_codes[i - 1], fpu_types[i - 1], sv);
		break;		/* XXX should return, but queue remains */

	case FSR_TT_UNFIN:
	case FSR_TT_UNIMP:
		if (fs->fs_qsize == 0)
			panic("fpu_cleanup 2");
		break;

	case FSR_TT_SEQ:
		panic("fpu sequence error");
		/* NOTREACHED */

	case FSR_TT_HWERR:
		log(LOG_ERR, "fpu hardware error (%s[%d])\n",
		    p->p_comm, p->p_pid);
		uprintf("%s[%d]: fpu hardware error\n", p->p_comm, p->p_pid);
		trapsignal(p, SIGFPE, -1, FPE_FLTINV, sv);	/* ??? */
		goto out;

	default:
		printf("fsr=0x%x\n", fsr);
		panic("fpu error");
	}

	/* emulate the instructions left in the queue */
	fe.fe_fpstate = fs;
	for (i = 0; i < fs->fs_qsize; i++) {
		instr.i_int = fs->fs_queue[i].fq_instr;
		if (instr.i_any.i_op != IOP_reg ||
		    (instr.i_op3.i_op3 != IOP3_FPop1 &&
		     instr.i_op3.i_op3 != IOP3_FPop2))
			panic("bogus fpu queue");
		error = fpu_execute(&fe, instr);
		switch (error) {

		case 0:
			continue;

		case FPE:
			trapsignal(p, SIGFPE,
			    fpu_codes[(fs->fs_fsr & FSR_CX) - 1],
			    fpu_types[(fs->fs_fsr & FSR_CX) - 1], sv);
			break;

		case NOTFPU:
			trapsignal(p, SIGILL, 0, ILL_COPROC, sv);
			break;

		default:
			panic("fpu_cleanup 3");
			/* NOTREACHED */
		}
		/* XXX should stop here, but queue remains */
	}
out:
	fs->fs_qsize = 0;
}

#ifdef notyet
/*
 * If we have no FPU at all (are there any machines like this out
 * there!?) we have to emulate each instruction, and we need a pointer
 * to the trapframe so that we can step over them and do FBfcc's.
 * We know the `queue' is empty, though; we just want to emulate
 * the instruction at tf->tf_pc.
 */
fpu_emulate(p, tf, fs)
	struct proc *p;
	register struct trapframe *tf;
	register struct fpstate *fs;
{

	do {
		fetch instr from pc
		decode
		if (integer instr) {
			/*
			 * We do this here, rather than earlier, to avoid
			 * losing even more badly than usual.
			 */
			if (p->p_addr->u_pcb.pcb_uw) {
				write_user_windows();
				if (rwindow_save(p))
					sigexit(p, SIGILL);
			}
			if (loadstore) {
				do_it;
				pc = npc, npc += 4
			} else if (fbfcc) {
				do_annul_stuff;
			} else
				return;
		} else if (fpu instr) {
			fe.fe_fsr = fs->fs_fsr &= ~FSR_CX;
			error = fpu_execute(&fe, fs, instr);
			switch (error) {
				etc;
			}
		} else
			return;
		if (want to reschedule)
			return;
	} while (error == 0);
}
#endif

/*
 * Execute an FPU instruction (one that runs entirely in the FPU; not
 * FBfcc or STF, for instance).  On return, fe->fe_fs->fs_fsr will be
 * modified to reflect the setting the hardware would have left.
 *
 * Note that we do not catch all illegal opcodes, so you can, for instance,
 * multiply two integers this way.
 */
int
fpu_execute(fe, instr)
	register struct fpemu *fe;
	union instr instr;
{
	register struct fpn *fp;
	register int opf, rs1, rs2, rd, type, mask, fsr, cx;
	register struct fpstate *fs;
	u_int space[4];

	/*
	 * `Decode' and execute instruction.  Start with no exceptions.
	 * The type of any i_opf opcode is in the bottom two bits, so we
	 * squish them out here.
	 */
	opf = instr.i_opf.i_opf;
	type = opf & 3;
	mask = "\0\0\1\3"[type];
	rs1 = instr.i_opf.i_rs1 & ~mask;
	rs2 = instr.i_opf.i_rs2 & ~mask;
	rd = instr.i_opf.i_rd & ~mask;
#ifdef notdef
	if ((rs1 | rs2 | rd) & mask)
		return (BADREG);
#endif
	fs = fe->fe_fpstate;
	fe->fe_fsr = fs->fs_fsr & ~FSR_CX;
	fe->fe_cx = 0;
	switch (opf >>= 2) {

	default:
		return (NOTFPU);

	case FMOV >> 2:		/* these should all be pretty obvious */
		rs1 = fs->fs_regs[rs2];
		goto mov;

	case FNEG >> 2:
		rs1 = fs->fs_regs[rs2] ^ (1U << 31);
		goto mov;

	case FABS >> 2:
		rs1 = fs->fs_regs[rs2] & ~(1U << 31);
	mov:
		fs->fs_regs[rd] = rs1;
		fs->fs_fsr = fe->fe_fsr;
		return (0);	/* success */

	case FSQRT >> 2:
		fpu_explode(fe, &fe->fe_f1, type, rs2);
		fp = fpu_sqrt(fe);
		break;

	case FADD >> 2:
		fpu_explode(fe, &fe->fe_f1, type, rs1);
		fpu_explode(fe, &fe->fe_f2, type, rs2);
		fp = fpu_add(fe);
		break;

	case FSUB >> 2:
		fpu_explode(fe, &fe->fe_f1, type, rs1);
		fpu_explode(fe, &fe->fe_f2, type, rs2);
		fp = fpu_sub(fe);
		break;

	case FMUL >> 2:
		fpu_explode(fe, &fe->fe_f1, type, rs1);
		fpu_explode(fe, &fe->fe_f2, type, rs2);
		fp = fpu_mul(fe);
		break;

	case FDIV >> 2:
		fpu_explode(fe, &fe->fe_f1, type, rs1);
		fpu_explode(fe, &fe->fe_f2, type, rs2);
		fp = fpu_div(fe);
		break;

	case FCMP >> 2:
		fpu_explode(fe, &fe->fe_f1, type, rs1);
		fpu_explode(fe, &fe->fe_f2, type, rs2);
		fpu_compare(fe, 0);
		goto cmpdone;

	case FCMPE >> 2:
		fpu_explode(fe, &fe->fe_f1, type, rs1);
		fpu_explode(fe, &fe->fe_f2, type, rs2);
		fpu_compare(fe, 1);
	cmpdone:
		/*
		 * The only possible exception here is NV; catch it
		 * early and get out, as there is no result register.
		 */
		cx = fe->fe_cx;
		fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT);
		if (cx != 0) {
			if (fsr & (FSR_NV << FSR_TEM_SHIFT)) {
				fs->fs_fsr = (fsr & ~FSR_FTT) |
				    (FSR_TT_IEEE << FSR_FTT_SHIFT);
				return (FPE);
			}
			fsr |= FSR_NV << FSR_AX_SHIFT;
		}
		fs->fs_fsr = fsr;
		return (0);

	case FSMULD >> 2:
	case FDMULX >> 2:
		if (type == FTYPE_EXT)
			return (NOTFPU);
		fpu_explode(fe, &fe->fe_f1, type, rs1);
		fpu_explode(fe, &fe->fe_f2, type, rs2);
		type++;	/* single to double, or double to quad */
		fp = fpu_mul(fe);
		break;

	case FTOI >> 2:
	case FTOS >> 2:
		rd = instr.i_opf.i_rd;
		goto fto;
	case FTOD >> 2:
		rd = instr.i_opf.i_rd & (~1);
		goto fto;
	case FTOX >> 2:
		rd = instr.i_opf.i_rd & (~3);

fto:
		fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
		type = opf & 3;	/* sneaky; depends on instruction encoding */
		break;
	}

	/*
	 * ALU operation is complete.  Collapse the result and then check
	 * for exceptions.  If we got any, and they are enabled, do not
	 * alter the destination register, just stop with an exception.
	 * Otherwise set new current exceptions and accrue.
	 */
	fpu_implode(fe, fp, type, space);
	cx = fe->fe_cx;
	fsr = fe->fe_fsr;
	if (cx != 0) {
		mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
		if (cx & mask) {
			/* not accrued??? */
			fs->fs_fsr = (fsr & ~FSR_FTT) |
			    (FSR_TT_IEEE << FSR_FTT_SHIFT) |
			    (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT);
			return (FPE);
		}
		fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT);
	}
	fs->fs_fsr = fsr;
	fs->fs_regs[rd] = space[0];
	if (type >= FTYPE_DBL) {
		fs->fs_regs[rd + 1] = space[1];
		if (type > FTYPE_DBL) {
			fs->fs_regs[rd + 2] = space[2];
			fs->fs_regs[rd + 3] = space[3];
		}
	}
	return (0);	/* success */
}


code:
/*	$OpenBSD: fpu_add.c,v 1.4 2003/06/02 23:27:54 millert Exp $	*/
/*	$NetBSD: fpu_add.c,v 1.3 1996/03/14 19:41:52 christos Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fpu_add.c	8.1 (Berkeley) 6/11/93
 */

/*
 * Perform an FPU add (return x + y).
 *
 * To subtract, negate y and call add.
 */

#include <sys/types.h>
#ifdef DIAGNOSTIC
#include <sys/systm.h>
#endif

#include <machine/reg.h>
#include <machine/instr.h>

#include <sparc/fpu/fpu_arith.h>
#include <sparc/fpu/fpu_emu.h>
#include <sparc/fpu/fpu_extern.h>

struct fpn *
fpu_add(fe)
	register struct fpemu *fe;
{
	register struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2, *r;
	register u_int r0, r1, r2, r3;
	register int rd;

	/*
	 * Put the `heavier' operand on the right (see fpu_emu.h).
	 * Then we will have one of the following cases, taken in the
	 * following order:
	 *
	 *  - y = NaN.  Implied: if only one is a signalling NaN, y is.
	 *	The result is y.
	 *  - y = Inf.  Implied: x != NaN (is 0, number, or Inf: the NaN
	 *    case was taken care of earlier).
	 *	If x = -y, the result is NaN.  Otherwise the result
	 *	is y (an Inf of whichever sign).
	 *  - y is 0.  Implied: x = 0.
	 *	If x and y differ in sign (one positive, one negative),
	 *	the result is +0 except when rounding to -Inf.  If same:
	 *	+0 + +0 = +0; -0 + -0 = -0.
	 *  - x is 0.  Implied: y != 0.
	 *	Result is y.
	 *  - other.  Implied: both x and y are numbers.
	 *	Do addition a la Hennessey & Patterson.
	 */
	ORDER(x, y);
	if (ISNAN(y))
		return (y);
	if (ISINF(y)) {
		if (ISINF(x) && x->fp_sign != y->fp_sign)
			return (fpu_newnan(fe));
		return (y);
	}
	rd = ((fe->fe_fsr >> FSR_RD_SHIFT) & FSR_RD_MASK);
	if (ISZERO(y)) {
		if (rd != FSR_RD_RM)	/* only -0 + -0 gives -0 */
			y->fp_sign &= x->fp_sign;
		else			/* any -0 operand gives -0 */
			y->fp_sign |= x->fp_sign;
		return (y);
	}
	if (ISZERO(x))
		return (y);
	/*
	 * We really have two numbers to add, although their signs may
	 * differ.  Make the exponents match, by shifting the smaller
	 * number right (e.g., 1.011 => 0.1011) and increasing its
	 * exponent (2^3 => 2^4).  Note that we do not alter the exponents
	 * of x and y here.
	 */
	r = &fe->fe_f3;
	r->fp_class = FPC_NUM;
	if (x->fp_exp == y->fp_exp) {
		r->fp_exp = x->fp_exp;
		r->fp_sticky = 0;
	} else {
		if (x->fp_exp < y->fp_exp) {
			/*
			 * Try to avoid subtract case iii (see below).
			 * This also guarantees that x->fp_sticky = 0.
			 */
			SWAP(x, y);
		}
		/* now x->fp_exp > y->fp_exp */
		r->fp_exp = x->fp_exp;
		r->fp_sticky = fpu_shr(y, x->fp_exp - y->fp_exp);
	}
	r->fp_sign = x->fp_sign;
	if (x->fp_sign == y->fp_sign) {
		FPU_DECL_CARRY

		/*
		 * The signs match, so we simply add the numbers.  The result
		 * may be `supernormal' (as big as 1.111...1 + 1.111...1, or
		 * 11.111...0).  If so, a single bit shift-right will fix it
		 * (but remember to adjust the exponent).
		 */
		/* r->fp_mant = x->fp_mant + y->fp_mant */
		FPU_ADDS(r->fp_mant[3], x->fp_mant[3], y->fp_mant[3]);
		FPU_ADDCS(r->fp_mant[2], x->fp_mant[2], y->fp_mant[2]);
		FPU_ADDCS(r->fp_mant[1], x->fp_mant[1], y->fp_mant[1]);
		FPU_ADDC(r0, x->fp_mant[0], y->fp_mant[0]);
		if ((r->fp_mant[0] = r0) >= FP_2) {
			(void) fpu_shr(r, 1);
			r->fp_exp++;
		}
	} else {
		FPU_DECL_CARRY

		/*
		 * The signs differ, so things are rather more difficult.
		 * H&P would have us negate the negative operand and add;
		 * this is the same as subtracting the negative operand.
		 * This is quite a headache.  Instead, we will subtract
		 * y from x, regardless of whether y itself is the negative
		 * operand.  When this is done one of three conditions will
		 * hold, depending on the magnitudes of x and y:
		 *   case i)   |x| > |y|.  The result is just x - y,
		 *	with x's sign, but it may need to be normalized.
		 *   case ii)  |x| = |y|.  The result is 0 (maybe -0)
		 *	so must be fixed up.
		 *   case iii) |x| < |y|.  We goofed; the result should
		 *	be (y - x), with the same sign as y.
		 * We could compare |x| and |y| here and avoid case iii,
		 * but that would take just as much work as the subtract.
		 * We can tell case iii has occurred by an overflow.
		 *
		 * N.B.: since x->fp_exp >= y->fp_exp, x->fp_sticky = 0.
		 */
		/* r->fp_mant = x->fp_mant - y->fp_mant */
		FPU_SET_CARRY(y->fp_sticky);
		FPU_SUBCS(r3, x->fp_mant[3], y->fp_mant[3]);
		FPU_SUBCS(r2, x->fp_mant[2], y->fp_mant[2]);
		FPU_SUBCS(r1, x->fp_mant[1], y->fp_mant[1]);
		FPU_SUBC(r0, x->fp_mant[0], y->fp_mant[0]);
		if (r0 < FP_2) {
			/* cases i and ii */
			if ((r0 | r1 | r2 | r3) == 0) {
				/* case ii */
				r->fp_class = FPC_ZERO;
				r->fp_sign = rd == FSR_RD_RM;
				return (r);
			}
		} else {
			/*
			 * Oops, case iii.  This can only occur when the
			 * exponents were equal, in which case neither
			 * x nor y have sticky bits set.  Flip the sign
			 * (to y's sign) and negate the result to get y - x.
			 */
#ifdef DIAGNOSTIC
			if (x->fp_exp != y->fp_exp || r->fp_sticky)
				panic("fpu_add");
#endif
			r->fp_sign = y->fp_sign;
			FPU_SUBS(r3, 0, r3);
			FPU_SUBCS(r2, 0, r2);
			FPU_SUBCS(r1, 0, r1);
			FPU_SUBC(r0, 0, r0);
		}
		r->fp_mant[3] = r3;
		r->fp_mant[2] = r2;
		r->fp_mant[1] = r1;
		r->fp_mant[0] = r0;
		if (r0 < FP_1)
			fpu_norm(r);
	}
	return (r);
}


code:
/*	$OpenBSD: fpu_arith.h,v 1.4 2011/09/17 08:38:07 miod Exp $	*/
/*	$NetBSD: fpu_arith.h,v 1.2 1994/11/20 20:52:35 deraadt Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fpu_arith.h	8.1 (Berkeley) 6/11/93
 */

/*
 * Extended-precision arithmetic.
 *
 * We hold the notion of a `carry register', which may or may not be a
 * machine carry bit or register.  On the SPARC, it is just the machine's
 * carry bit.
 *
 * In the worst case, you can compute the carry from x+y as
 *	(unsigned)(x + y) < (unsigned)x
 * and from x+y+c as
 *	((unsigned)(x + y + c) <= (unsigned)x && (y|c) != 0)
 * for example.
 */

/* set up for extended-precision arithmetic */
#define	FPU_DECL_CARRY

/*
 * We have three kinds of add:
 *	add with carry:					  r = x + y + c
 *	add (ignoring current carry) and set carry:	c'r = x + y + 0
 *	add with carry and set carry:			c'r = x + y + c
 * The macros use `C' for `use carry' and `S' for `set carry'.
 * Note that the state of the carry is undefined after ADDC and SUBC,
 * so if all you have for these is `add with carry and set carry',
 * that is OK.
 *
 * The same goes for subtract, except that we compute x - y - c.
 *
 * Finally, we have a way to get the carry into a `regular' variable,
 * or set it from a value.  SET_CARRY turns 0 into no-carry, nonzero
 * into carry; GET_CARRY sets its argument to 0 or 1.
 */
#define	FPU_ADDC(r, x, y) \
	asm volatile("addx %1,%2,%0" : "=r"(r) : "r"(x), "r"(y))
#define	FPU_ADDS(r, x, y) \
	asm volatile("addcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y))
#define	FPU_ADDCS(r, x, y) \
	asm volatile("addxcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y))
#define	FPU_SUBC(r, x, y) \
	asm volatile("subx %1,%2,%0" : "=r"(r) : "r"(x), "r"(y))
#define	FPU_SUBS(r, x, y) \
	asm volatile("subcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y))
#define	FPU_SUBCS(r, x, y) \
	asm volatile("subxcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y))

#define	FPU_GET_CARRY(r) asm volatile("addx %%g0,%%g0,%0" : "=r"(r))
#define	FPU_SET_CARRY(v) asm volatile("addcc %0,-1,%%g0" : : "r"(v))

#define	FPU_SHL1_BY_ADD	/* shift left 1 faster by ADDC than (a<<1)|(b>>31) */


code:
/*	$OpenBSD: fpu_compare.c,v 1.3 2003/06/02 23:27:54 millert Exp $	*/
/*	$NetBSD: fpu_compare.c,v 1.2 1994/11/20 20:52:37 deraadt Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fpu_compare.c	8.1 (Berkeley) 6/11/93
 */

/*
 * CMP and CMPE instructions.
 *
 * These rely on the fact that our internal wide format is achieved by
 * adding zero bits to the end of narrower mantissas.
 */

#include <sys/types.h>

#include <machine/reg.h>

#include <sparc/fpu/fpu_arith.h>
#include <sparc/fpu/fpu_emu.h>

/*
 * Perform a compare instruction (with or without unordered exception).
 * This updates the fcc field in the fsr.
 *
 * If either operand is NaN, the result is unordered.  For cmpe, this
 * causes an NV exception.  Everything else is ordered:
 *	|Inf| > |numbers| > |0|.
 * We already arranged for fp_class(Inf) > fp_class(numbers) > fp_class(0),
 * so we get this directly.  Note, however, that two zeros compare equal
 * regardless of sign, while everything else depends on sign.
 *
 * Incidentally, two Infs of the same sign compare equal (per the 80387
 * manual---it would be nice if the SPARC documentation were more
 * complete).
 */
void
fpu_compare(struct fpemu *fe, int cmpe)
{
	register struct fpn *a, *b;
	register int cc, r3, r2, r1, r0;
	FPU_DECL_CARRY

	a = &fe->fe_f1;
	b = &fe->fe_f2;

	if (ISNAN(a) || ISNAN(b)) {
		/*
		 * In any case, we already got an exception for signalling
		 * NaNs; here we may replace that one with an identical
		 * exception, but so what?.
		 */
		if (cmpe)
			fe->fe_cx = FSR_NV;
		cc = FSR_CC_UO;
		goto done;
	}

	/*
	 * Must handle both-zero early to avoid sign goofs.  Otherwise,
	 * at most one is 0, and if the signs differ we are done.
	 */
	if (ISZERO(a) && ISZERO(b)) {
		cc = FSR_CC_EQ;
		goto done;
	}
	if (a->fp_sign) {		/* a < 0 (or -0) */
		if (!b->fp_sign) {	/* b >= 0 (or if a = -0, b > 0) */
			cc = FSR_CC_LT;
			goto done;
		}
	} else {			/* a > 0 (or +0) */
		if (b->fp_sign) {	/* b <= -0 (or if a = +0, b < 0) */
			cc = FSR_CC_GT;
			goto done;
		}
	}

	/*
	 * Now the signs are the same (but may both be negative).  All
	 * we have left are these cases:
	 *
	 *	|a| < |b|		[classes or values differ]
	 *	|a| > |b|		[classes or values differ]
	 *	|a| == |b|		[classes and values identical]
	 *
	 * We define `diff' here to expand these as:
	 *
	 *	|a| < |b|, a,b >= 0: a < b => FSR_CC_LT
	 *	|a| < |b|, a,b < 0:  a > b => FSR_CC_GT
	 *	|a| > |b|, a,b >= 0: a > b => FSR_CC_GT
	 *	|a| > |b|, a,b < 0:  a < b => FSR_CC_LT
	 */
#define opposite_cc(cc) ((cc) == FSR_CC_LT ? FSR_CC_GT : FSR_CC_LT)
#define	diff(magnitude) (a->fp_sign ? opposite_cc(magnitude) :  (magnitude))
	if (a->fp_class < b->fp_class) {	/* |a| < |b| */
		cc = diff(FSR_CC_LT);
		goto done;
	}
	if (a->fp_class > b->fp_class) {	/* |a| > |b| */
		cc = diff(FSR_CC_GT);
		goto done;
	}
	/* now none can be 0: only Inf and numbers remain */
	if (ISINF(a)) {				/* |Inf| = |Inf| */
		cc = FSR_CC_EQ;
		goto done;
	}
	/*
	 * Only numbers remain.  To compare two numbers in magnitude, we
	 * simply subtract their mantissas.
	 */
	FPU_SUBS(r3, a->fp_mant[0], b->fp_mant[0]);
	FPU_SUBCS(r2, a->fp_mant[1], b->fp_mant[1]);
	FPU_SUBCS(r1, a->fp_mant[2], b->fp_mant[2]);
	FPU_SUBC(r0, a->fp_mant[3], b->fp_mant[3]);
	if (r0 < 0)				/* underflow: |a| < |b| */
		cc = diff(FSR_CC_LT);
	else if ((r0 | r1 | r2 | r3) != 0)	/* |a| > |b| */
		cc = diff(FSR_CC_GT);
	else
		cc = FSR_CC_EQ;		/* |a| == |b| */
done:
	fe->fe_fsr = (fe->fe_fsr & ~FSR_FCC) | (cc << FSR_FCC_SHIFT);
}


code:
/*	$OpenBSD: fpu_div.c,v 1.4 2013/11/26 20:33:14 deraadt Exp $	*/
/*	$NetBSD: fpu_div.c,v 1.2 1994/11/20 20:52:38 deraadt Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fpu_div.c	8.1 (Berkeley) 6/11/93
 */

/*
 * Perform an FPU divide (return x / y).
 */

#include <sys/types.h>

#include <machine/reg.h>

#include <sparc/fpu/fpu_arith.h>
#include <sparc/fpu/fpu_emu.h>

/*
 * Division of normal numbers is done as follows:
 *
 * x and y are floating point numbers, i.e., in the form 1.bbbb * 2^e.
 * If X and Y are the mantissas (1.bbbb's), the quotient is then:
 *
 *	q = (X / Y) * 2^((x exponent) - (y exponent))
 *
 * Since X and Y are both in [1.0,2.0), the quotient's mantissa (X / Y)
 * will be in [0.5,2.0).  Moreover, it will be less than 1.0 if and only
 * if X < Y.  In that case, it will have to be shifted left one bit to
 * become a normal number, and the exponent decremented.  Thus, the
 * desired exponent is:
 *
 *	left_shift = x->fp_mant < y->fp_mant;
 *	result_exp = x->fp_exp - y->fp_exp - left_shift;
 *
 * The quotient mantissa X/Y can then be computed one bit at a time
 * using the following algorithm:
 *
 *	Q = 0;			-- Initial quotient.
 *	R = X;			-- Initial remainder,
 *	if (left_shift)		--   but fixed up in advance.
 *		R *= 2;
 *	for (bit = FP_NMANT; --bit >= 0; R *= 2) {
 *		if (R >= Y) {
 *			Q |= 1U << bit;
 *			R -= Y;
 *		}
 *	}
 *
 * The subtraction R -= Y always removes the uppermost bit from R (and
 * can sometimes remove additional lower-order 1 bits); this proof is
 * left to the reader.
 *
 * This loop correctly calculates the guard and round bits since they are
 * included in the expanded internal representation.  The sticky bit
 * is to be set if and only if any other bits beyond guard and round
 * would be set.  From the above it is obvious that this is true if and
 * only if the remainder R is nonzero when the loop terminates.
 *
 * Examining the loop above, we can see that the quotient Q is built
 * one bit at a time ``from the top down''.  This means that we can
 * dispense with the multi-word arithmetic and just build it one word
 * at a time, writing each result word when it is done.
 *
 * Furthermore, since X and Y are both in [1.0,2.0), we know that,
 * initially, R >= Y.  (Recall that, if X < Y, R is set to X * 2 and
 * is therefore at in [2.0,4.0).)  Thus Q is sure to have bit FP_NMANT-1
 * set, and R can be set initially to either X - Y (when X >= Y) or
 * 2X - Y (when X < Y).  In addition, comparing R and Y is difficult,
 * so we will simply calculate R - Y and see if that underflows.
 * This leads to the following revised version of the algorithm:
 *
 *	R = X;
 *	bit = FP_1;
 *	D = R - Y;
 *	if (D >= 0) {
 *		result_exp = x->fp_exp - y->fp_exp;
 *		R = D;
 *		q = bit;
 *		bit >>= 1;
 *	} else {
 *		result_exp = x->fp_exp - y->fp_exp - 1;
 *		q = 0;
 *	}
 *	R <<= 1;
 *	do  {
 *		D = R - Y;
 *		if (D >= 0) {
 *			q |= bit;
 *			R = D;
 *		}
 *		R <<= 1;
 *	} while ((bit >>= 1) != 0);
 *	Q[0] = q;
 *	for (i = 1; i < 4; i++) {
 *		q = 0, bit = 1U << 31;
 *		do {
 *			D = R - Y;
 *			if (D >= 0) {
 *				q |= bit;
 *				R = D;
 *			}
 *			R <<= 1;
 *		} while ((bit >>= 1) != 0);
 *		Q[i] = q;
 *	}
 *
 * This can be refined just a bit further by moving the `R <<= 1'
 * calculations to the front of the do-loops and eliding the first one.
 * The process can be terminated immediately whenever R becomes 0, but
 * this is relatively rare, and we do not bother.
 */

struct fpn *
fpu_div(fe)
	register struct fpemu *fe;
{
	register struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2;
	register u_int q, bit;
	register u_int r0, r1, r2, r3, d0, d1, d2, d3, y0, y1, y2, y3;
	FPU_DECL_CARRY

	/*
	 * Since divide is not commutative, we cannot just use ORDER.
	 * Check either operand for NaN first; if there is at least one,
	 * order the signalling one (if only one) onto the right, then
	 * return it.  Otherwise we have the following cases:
	 *
	 *	Inf / Inf = NaN, plus NV exception
	 *	Inf / num = Inf [i.e., return x]
	 *	Inf / 0   = Inf [i.e., return x]
	 *	0 / Inf = 0 [i.e., return x]
	 *	0 / num = 0 [i.e., return x]
	 *	0 / 0   = NaN, plus NV exception
	 *	num / Inf = 0
	 *	num / num = num (do the divide)
	 *	num / 0   = Inf, plus DZ exception
	 */
	if (ISNAN(x) || ISNAN(y)) {
		ORDER(x, y);
		return (y);
	}
	if (ISINF(x) || ISZERO(x)) {
		if (x->fp_class == y->fp_class)
			return (fpu_newnan(fe));
		return (x);
	}

	/* all results at this point use XOR of operand signs */
	x->fp_sign ^= y->fp_sign;
	if (ISINF(y)) {
		x->fp_class = FPC_ZERO;
		return (x);
	}
	if (ISZERO(y)) {
		fe->fe_cx = FSR_DZ;
		x->fp_class = FPC_INF;
		return (x);
	}

	/*
	 * Macros for the divide.  See comments at top for algorithm.
	 * Note that we expand R, D, and Y here.
	 */

#define	SUBTRACT		/* D = R - Y */ \
	FPU_SUBS(d3, r3, y3); FPU_SUBCS(d2, r2, y2); \
	FPU_SUBCS(d1, r1, y1); FPU_SUBC(d0, r0, y0)

#define	NONNEGATIVE		/* D >= 0 */ \
	((int)d0 >= 0)

#ifdef FPU_SHL1_BY_ADD
#define	SHL1			/* R <<= 1 */ \
	FPU_ADDS(r3, r3, r3); FPU_ADDCS(r2, r2, r2); \
	FPU_ADDCS(r1, r1, r1); FPU_ADDC(r0, r0, r0)
#else
#define	SHL1 \
	r0 = (r0 << 1) | (r1 >> 31), r1 = (r1 << 1) | (r2 >> 31), \
	r2 = (r2 << 1) | (r3 >> 31), r3 <<= 1
#endif

#define	LOOP			/* do ... while (bit >>= 1) */ \
	do { \
		SHL1; \
		SUBTRACT; \
		if (NONNEGATIVE) { \
			q |= bit; \
			r0 = d0, r1 = d1, r2 = d2, r3 = d3; \
		} \
	} while ((bit >>= 1) != 0)

#define	WORD(r, i)			/* calculate r->fp_mant[i] */ \
	q = 0; \
	bit = 1U << 31; \
	LOOP; \
	(x)->fp_mant[i] = q

	/* Setup.  Note that we put our result in x. */
	r0 = x->fp_mant[0];
	r1 = x->fp_mant[1];
	r2 = x->fp_mant[2];
	r3 = x->fp_mant[3];
	y0 = y->fp_mant[0];
	y1 = y->fp_mant[1];
	y2 = y->fp_mant[2];
	y3 = y->fp_mant[3];

	bit = FP_1;
	SUBTRACT;
	if (NONNEGATIVE) {
		x->fp_exp -= y->fp_exp;
		r0 = d0, r1 = d1, r2 = d2, r3 = d3;
		q = bit;
		bit >>= 1;
	} else {
		x->fp_exp -= y->fp_exp + 1;
		q = 0;
	}
	LOOP;
	x->fp_mant[0] = q;
	WORD(x, 1);
	WORD(x, 2);
	WORD(x, 3);
	x->fp_sticky = r0 | r1 | r2 | r3;

	return (x);
}


code:
/*	$OpenBSD: fpu_emu.h,v 1.3 2003/06/02 23:27:54 millert Exp $	*/
/*	$NetBSD: fpu_emu.h,v 1.2 1994/11/20 20:52:39 deraadt Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fpu_emu.h	8.1 (Berkeley) 6/11/93
 */

/*
 * Floating point emulator (tailored for SPARC, but structurally
 * machine-independent).
 *
 * Floating point numbers are carried around internally in an `expanded'
 * or `unpacked' form consisting of:
 *	- sign
 *	- unbiased exponent
 *	- mantissa (`1.' + 112-bit fraction + guard + round)
 *	- sticky bit
 * Any implied `1' bit is inserted, giving a 113-bit mantissa that is
 * always nonzero.  Additional low-order `guard' and `round' bits are
 * scrunched in, making the entire mantissa 115 bits long.  This is divided
 * into four 32-bit words, with `spare' bits left over in the upper part
 * of the top word (the high bits of fp_mant[0]).  An internal `exploded'
 * number is thus kept within the half-open interval [1.0,2.0) (but see
 * the `number classes' below).  This holds even for denormalized numbers:
 * when we explode an external denorm, we normalize it, introducing low-order
 * zero bits, so that the rest of the code always sees normalized values.
 *
 * Note that a number of our algorithms use the `spare' bits at the top.
 * The most demanding algorithm---the one for sqrt---depends on two such
 * bits, so that it can represent values up to (but not including) 8.0,
 * and then it needs a carry on top of that, so that we need three `spares'.
 *
 * The sticky-word is 32 bits so that we can use `OR' operators to goosh
 * whole words from the mantissa into it.
 *
 * All operations are done in this internal extended precision.  According
 * to Hennesey & Patterson, Appendix A, rounding can be repeated---that is,
 * it is OK to do a+b in extended precision and then round the result to
 * single precision---provided single, double, and extended precisions are
 * `far enough apart' (they always are), but we will try to avoid any such
 * extra work where possible.
 */
struct fpn {
	int	fp_class;		/* see below */
	int	fp_sign;		/* 0 => positive, 1 => negative */
	int	fp_exp;			/* exponent (unbiased) */
	int	fp_sticky;		/* nonzero bits lost at right end */
	u_int	fp_mant[4];		/* 115-bit mantissa */
};

#define	FP_NMANT	115		/* total bits in mantissa (incl g,r) */
#define	FP_NG		2		/* number of low-order guard bits */
#define	FP_LG		((FP_NMANT - 1) & 31)	/* log2(1.0) for fp_mant[0] */
#define	FP_QUIETBIT	(1 << (FP_LG - 1))	/* Quiet bit in NaNs (0.5) */
#define	FP_1		(1 << FP_LG)		/* 1.0 in fp_mant[0] */
#define	FP_2		(1 << (FP_LG + 1))	/* 2.0 in fp_mant[0] */

/*
 * Number classes.  Since zero, Inf, and NaN cannot be represented using
 * the above layout, we distinguish these from other numbers via a class.
 * In addition, to make computation easier and to follow Appendix N of
 * the SPARC Version 8 standard, we give each kind of NaN a separate class.
 */
#define	FPC_SNAN	-2		/* signalling NaN (sign irrelevant) */
#define	FPC_QNAN	-1		/* quiet NaN (sign irrelevant) */
#define	FPC_ZERO	0		/* zero (sign matters) */
#define	FPC_NUM		1		/* number (sign matters) */
#define	FPC_INF		2		/* infinity (sign matters) */

#define	ISNAN(fp)	((fp)->fp_class < 0)
#define	ISZERO(fp)	((fp)->fp_class == 0)
#define	ISINF(fp)	((fp)->fp_class == FPC_INF)

/*
 * ORDER(x,y) `sorts' a pair of `fpn *'s so that the right operand (y) points
 * to the `more significant' operand for our purposes.  Appendix N says that
 * the result of a computation involving two numbers are:
 *
 *	If both are SNaN: operand 2, converted to Quiet
 *	If only one is SNaN: the SNaN operand, converted to Quiet
 *	If both are QNaN: operand 2
 *	If only one is QNaN: the QNaN operand
 *
 * In addition, in operations with an Inf operand, the result is usually
 * Inf.  The class numbers are carefully arranged so that if
 *	(unsigned)class(op1) > (unsigned)class(op2)
 * then op1 is the one we want; otherwise op2 is the one we want.
 */
#define	ORDER(x, y) { \
	if ((u_int)(x)->fp_class > (u_int)(y)->fp_class) \
		SWAP(x, y); \
}
#define	SWAP(x, y) { \
	register struct fpn *swap; \
	swap = (x), (x) = (y), (y) = swap; \
}

/*
 * Emulator state.
 */
struct fpemu {
	struct	fpstate *fe_fpstate;	/* registers, etc */
	int	fe_fsr;			/* fsr copy (modified during op) */
	int	fe_cx;			/* exceptions */
	struct	fpn fe_f1;		/* operand 1 */
	struct	fpn fe_f2;		/* operand 2, if required */
	struct	fpn fe_f3;		/* available storage for result */
};

/*
 * Arithmetic functions.
 * Each of these may modify its inputs (f1,f2) and/or the temporary.
 * Each returns a pointer to the result and/or sets exceptions.
 */
struct	fpn *fpu_add(struct fpemu *);
#define	fpu_sub(fe) ((fe)->fe_f2.fp_sign ^= 1, fpu_add(fe))
struct	fpn *fpu_mul(struct fpemu *);
struct	fpn *fpu_div(struct fpemu *);
struct	fpn *fpu_sqrt(struct fpemu *);

/*
 * Other functions.
 */

/* Perform a compare instruction (with or without unordered exception). */
void	fpu_compare(struct fpemu *, int);

/* Build a new Quiet NaN (sign=0, frac=all 1's). */
struct	fpn *fpu_newnan(struct fpemu *);

/*
 * Shift a number right some number of bits, taking care of round/sticky.
 * Note that the result is probably not a well-formed number (it will lack
 * the normal 1-bit mant[0]&FP_1).
 */
int	fpu_shr(struct fpn *, int);

/* Conversion to and from internal format -- note asymmetry. */
int	fpu_itofpn(struct fpn *, u_int);
int	fpu_stofpn(struct fpn *, u_int);
int	fpu_dtofpn(struct fpn *, u_int, u_int);
int	fpu_xtofpn(struct fpn *, u_int, u_int, u_int, u_int);

u_int	fpu_fpntoi(struct fpemu *, struct fpn *);
u_int	fpu_fpntos(struct fpemu *, struct fpn *);
u_int	fpu_fpntod(struct fpemu *, struct fpn *);
u_int	fpu_fpntox(struct fpemu *, struct fpn *);

void	fpu_explode(struct fpemu *, struct fpn *, int, int);
void	fpu_implode(struct fpemu *, struct fpn *, int, u_int *);


code:
/*	$OpenBSD: fpu_explode.c,v 1.4 2003/06/02 23:27:54 millert Exp $	*/
/*	$NetBSD: fpu_explode.c,v 1.3 1996/03/14 19:41:54 christos Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)fpu_explode.c	8.1 (Berkeley) 6/11/93
 */

/*
 * FPU subroutines: `explode' the machine's `packed binary' format numbers
 * into our internal format.
 */

#include <sys/types.h>
#include <sys/systm.h>

#include <machine/ieee.h>
#include <machine/instr.h>
#include <machine/reg.h>

#include <sparc/fpu/fpu_arith.h>
#include <sparc/fpu/fpu_emu.h>
#include <sparc/fpu/fpu_extern.h>

/*
 * N.B.: in all of the following, we assume the FP format is
 *
 *	---------------------------
 *	| s | exponent | fraction |
 *	---------------------------
 *
 * (which represents -1**s * 1.fraction * 2**exponent), so that the
 * sign bit is way at the top (bit 31), the exponent is next, and
 * then the remaining bits mark the fraction.  A zero exponent means
 * zero or denormalized (0.fraction rather than 1.fraction), and the
 * maximum possible exponent, 2bias+1, signals inf (fraction==0) or NaN.
 *
 * Since the sign bit is always the topmost bit---this holds even for
 * integers---we set that outside all the *tof functions.  Each function
 * returns the class code for the new number (but note that we use
 * FPC_QNAN for all NaNs; fpu_explode will fix this if appropriate).
 */

/*
 * int -> fpn.
 */
int
fpu_itof(fp, i)
	register struct fpn *fp;
	register u_int i;
{

	if (i == 0)
		return (FPC_ZERO);
	/*
	 * The value FP_1 represents 2^FP_LG, so set the exponent
	 * there and let normalization fix it up.  Convert negative
	 * numbers to sign-and-magnitude.  Note that this relies on
	 * fpu_norm()'s handling of `supernormals'; see fpu_subr.c.
	 */
	fp->fp_exp = FP_LG;
	fp->fp_mant[0] = (int)i < 0 ? -i : i;
	fp->fp_mant[1] = 0;
	fp->fp_mant[2] = 0;
	fp->fp_mant[3] = 0;
	fpu_norm(fp);
	return (FPC_NUM);
}

#define	mask(nbits) ((1 << (nbits)) - 1)

/*
 * All external floating formats convert to internal in the same manner,
 * as defined here.  Note that only normals get an implied 1.0 inserted.
 */
#define	FP_TOF(exp, expbias, allfrac, f0, f1, f2, f3) \
	if (exp == 0) { \
		if (allfrac == 0) \
			return (FPC_ZERO); \
		fp->fp_exp = 1 - expbias; \
		fp->fp_mant[0] = f0; \
		fp->fp_mant[1] = f1; \
		fp->fp_mant[2] = f2; \
		fp->fp_mant[3] = f3; \
		fpu_norm(fp); \
		return (FPC_NUM); \
	} \
	if (exp == (2 * expbias + 1)) { \
		if (allfrac == 0) \
			return (FPC_INF); \
		fp->fp_mant[0] = f0; \
		fp->fp_mant[1] = f1; \
		fp->fp_mant[2] = f2; \
		fp->fp_mant[3] = f3; \
		return (FPC_QNAN); \
	} \
	fp->fp_exp = exp - expbias; \
	fp->fp_mant[0] = FP_1 | f0; \
	fp->fp_mant[1] = f1; \
	fp->fp_mant[2] = f2; \
	fp->fp_mant[3] = f3; \
	return (FPC_NUM)

/*
 * 32-bit single precision -> fpn.
 * We assume a single occupies at most (64-FP_LG) bits in the internal
 * format: i.e., needs at most fp_mant[0] and fp_mant[1].
 */
int
fpu_stof(fp, i)
	register struct fpn *fp;
	register u_int i;
{
	register int exp;
	register u_int frac, f0, f1;
#define SNG_SHIFT (SNG_FRACBITS - FP_LG)

	exp = (i >> (32 - 1 - SNG_EXPBITS)) & mask(SNG_EXPBITS);
	frac = i & mask(SNG_FRACBITS);
	f0 = frac >> SNG_SHIFT;
	f1 = frac << (32 - SNG_SHIFT);
	FP_TOF(exp, SNG_EXP_BIAS, frac, f0, f1, 0, 0);
}

/*
 * 64-bit double -> fpn.
 * We assume this uses at most (96-FP_LG) bits.
 */
int
fpu_dtof(fp, i, j)
	register struct fpn *fp;
	register u_int i, j;
{
	register int exp;
	register u_int frac, f0, f1, f2;
#define DBL_SHIFT (DBL_FRACBITS - 32 - FP_LG)

	exp = (i >> (32 - 1 - DBL_EXPBITS)) & mask(DBL_EXPBITS);
	frac = i & mask(DBL_FRACBITS - 32);
	f0 = frac >> DBL_SHIFT;
	f1 = (frac << (32 - DBL_SHIFT)) | (j >> DBL_SHIFT);
	f2 = j << (32 - DBL_SHIFT);
	frac |= j;
	FP_TOF(exp, DBL_EXP_BIAS, frac, f0, f1, f2, 0);
}

/*
 * 128-bit extended -> fpn.
 */
int
fpu_xtof(fp, i, j, k, l)
	register struct fpn *fp;
	register u_int i, j, k, l;
{
	register int exp;
	register u_int frac, f0, f1, f2, f3;
#define EXT_SHIFT (-(EXT_FRACBITS - 3 * 32 - FP_LG))	/* left shift! */

	/*
	 * Note that ext and fpn `line up', hence no shifting needed.
	 */
	exp = (i >> (32 - 1 - EXT_EXPBITS)) & mask(EXT_EXPBITS);
	frac = i & mask(EXT_FRACBITS - 3 * 32);
	f0 = (frac << EXT_SHIFT) | (j >> (32 - EXT_SHIFT));
	f1 = (j << EXT_SHIFT) | (k >> (32 - EXT_SHIFT));
	f2 = (k << EXT_SHIFT) | (l >> (32 - EXT_SHIFT));
	f3 = l << EXT_SHIFT;
	frac |= j | k | l;
	FP_TOF(exp, EXT_EXP_BIAS, frac, f0, f1, f2, f3);
}

/*
 * Explode the contents of a register / regpair / regquad.
 * If the input is a signalling NaN, an NV (invalid) exception
 * will be set.  (Note that nothing but NV can occur until ALU
 * operations are performed.)
 */
void
fpu_explode(fe, fp, type, reg)
	register struct fpemu *fe;
	register struct fpn *fp;
	int type, reg;
{
	register u_int s, *space;

	space = &fe->fe_fpstate->fs_regs[reg];
	s = space[0];
	fp->fp_sign = s >> 31;
	fp->fp_sticky = 0;
	switch (type) {

	case FTYPE_INT:
		s = fpu_itof(fp, s);
		break;

	case FTYPE_SNG:
		s = fpu_stof(fp, s);
		break;

	case FTYPE_DBL:
		s = fpu_dtof(fp, s, space[1]);
		break;

	case FTYPE_EXT:
		s = fpu_xtof(fp, s, space[1], space[2], space[3]);
		break;

	default:
		panic("fpu_explode");
	}
	if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) {
		/*
		 * Input is a signalling NaN.  All operations that return
		 * an input NaN operand put it through a ``NaN conversion'',
		 * which basically just means ``turn on the quiet bit''.
		 * We do this here so that all NaNs internally look quiet
		 * (we can tell signalling ones by their class).
		 */
		fp->fp_mant[0] |= FP_QUIETBIT;
		fe->fe_cx = FSR_NV;	/* assert invalid operand */
		s = FPC_SNAN;
	}
	fp->fp_class = s;
}