* Copyright (c) 1992 The Regents of the University of California.
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
* %sccs.include.redist.c%
* @(#)fpu.c 7.1 (Berkeley) %G%
* from: $Header: fpu.c,v 1.2 92/06/17 05:41:27 torek Exp $
#include "machine/instr.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 X8(x) X4(x),X4(x)
#define X16(x) X8(x),X8(x)
static char cx_to_trapx
[] = {
static u_char fpu_codes
[] = {
* 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.
register struct fpstate
*fs
;
register int i
, fsr
= fs
->fs_fsr
, error
;
switch ((fsr
>> FSR_FTT_SHIFT
) & FSR_FTT_MASK
) {
panic("fpu_cleanup 1"); /* ??? */
/* XXX missing trap address! */
if ((i
= fsr
& FSR_CX
) == 0)
panic("fpu ieee trap, but no exception");
trapsignal(p
, SIGFPE
, fpu_codes
[i
- 1]);
break; /* XXX should return, but queue remains */
panic("fpu sequence error");
log(LOG_ERR
, "fpu hardware error (%s[%d])\n",
uprintf("%s[%d]: fpu hardware error\n", p
->p_comm
, p
->p_pid
);
trapsignal(p
, SIGFPE
, -1); /* ??? */
/* emulate the instructions left in the queue */
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
);
fpu_codes
[(fs
->fs_fsr
& FSR_CX
) - 1]);
trapsignal(p
, SIGILL
, 0); /* ??? code? */
/* XXX should stop here, but queue remains */
* 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.
register struct trapframe
*tf
;
register struct fpstate
*fs
;
* We do this here, rather than earlier, to avoid
* losing even more badly than usual.
if (p
->p_addr
->u_pcb
.pcb_uw
) {
fe
.fe_fsr
= fs
->fs_fsr
&= ~FSR_CX
;
error
= fpu_execute(&fe
, fs
, instr
);
* 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.
register struct fpemu
*fe
;
register int opf
, rs1
, rs2
, rd
, type
, mask
, fsr
, cx
;
register struct fpstate
*fs
;
* `Decode' and execute instruction. Start with no exceptions.
* The type of any i_opf opcode is in the bottom two bits, so we
rs1
= instr
.i_opf
.i_rs1
& ~mask
;
rs2
= instr
.i_opf
.i_rs2
& ~mask
;
rd
= instr
.i_opf
.i_rd
& ~mask
;
if ((rs1
| rs2
| rd
) & mask
)
fe
->fe_fsr
= fs
->fs_fsr
& ~FSR_CX
;
case FMOV
>> 2: /* these should all be pretty obvious */
rs1
= fs
->fs_regs
[rs2
] ^ (1 << 31);
rs1
= fs
->fs_regs
[rs2
] & ~(1 << 31);
return (0); /* success */
fpu_explode(fe
, &fe
->fe_f1
, type
, rs2
);
fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
* The only possible exception here is NV; catch it
* early and get out, as there is no result register.
fsr
= fe
->fe_fsr
| (cx
<< FSR_CX_SHIFT
);
if (fsr
& (FSR_NV
<< FSR_TEM_SHIFT
)) {
fs
->fs_fsr
= (fsr
& ~FSR_FTT
) |
(FSR_TT_IEEE
<< FSR_FTT_SHIFT
);
fsr
|= FSR_NV
<< FSR_AX_SHIFT
;
fpu_explode(fe
, &fe
->fe_f1
, type
, rs1
);
fpu_explode(fe
, &fe
->fe_f2
, type
, rs2
);
type
++; /* single to double, or double to quad */
fpu_explode(fe
, fp
= &fe
->fe_f1
, type
, rs2
);
type
= opf
& 3; /* sneaky; depends on instruction encoding */
* 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
);
mask
= (fsr
>> FSR_TEM_SHIFT
) & FSR_TEM_MASK
;
fs
->fs_fsr
= (fsr
& ~FSR_FTT
) |
(FSR_TT_IEEE
<< FSR_FTT_SHIFT
) |
(cx_to_trapx
[(cx
& mask
) - 1] << FSR_CX_SHIFT
);
fsr
|= (cx
<< FSR_CX_SHIFT
) | (cx
<< FSR_AX_SHIFT
);
fs
->fs_regs
[rd
] = space
[0];
fs
->fs_regs
[rd
+ 1] = space
[1];
fs
->fs_regs
[rd
+ 2] = space
[2];
fs
->fs_regs
[rd
+ 3] = space
[3];
return (0); /* success */