#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: fpu_emulate.c,v 1.49 2025/01/06 07:34:24 isaki Exp $");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/systm.h>
#include <machine/frame.h>
#if defined(DDB) && defined(DEBUG_FPE)
# include <m68k/db_machdep.h>
#endif
#include "fpu_emulate.h"
#define fpe_abort(tfp, ksi, signo, code) \
do { \
(ksi)->ksi_signo = (signo); \
(ksi)->ksi_code = (code); \
(ksi)->ksi_addr = (void *)(frame)->f_pc; \
return -1; \
} while ( 0)
static int fpu_emul_fmovmcr(struct fpemu *, struct instruction *);
static int fpu_emul_fmovm(struct fpemu *, struct instruction *);
static int fpu_emul_arith(struct fpemu *, struct instruction *);
static int fpu_emul_type1(struct fpemu *, struct instruction *);
static int fpu_emul_brcc(struct fpemu *, struct instruction *);
static int test_cc(struct fpemu *, int);
#ifdef DEBUG_FPE
#define DUMP_INSN(insn) \
printf("%s: insn={adv=%d,siz=%d,op=%04x,w1=%04x}\n", \
__func__, \
(insn)->is_advance, (insn)->is_datasize, \
(insn)->is_opcode, (insn)->is_word1)
#define DPRINTF(x) printf x
#else
#define DUMP_INSN(insn) do {} while ( 0)
#define DPRINTF(x) do {} while ( 0)
#endif
int
fpu_emulate(struct frame *frame, struct fpframe *fpf, ksiginfo_t *ksi)
{
static struct instruction insn;
static struct fpemu fe;
int optype, sig;
unsigned short sval;
insn.is_datasize = -1;
fe.fe_frame = frame;
fe.fe_fpframe = fpf;
fe.fe_fpsr = fpf->fpf_fpsr;
fe.fe_fpcr = fpf->fpf_fpcr;
DPRINTF(("%s: ENTERING: FPSR=%08x, FPCR=%08x\n",
__func__, fe.fe_fpsr, fe.fe_fpcr));
insn.is_pc = frame->f_pc;
insn.is_nextpc = 0;
if (frame->f_format == 4) {
#if 0
insn.is_nextpc = frame->f_pc;
#endif
insn.is_pc = frame->f_fmt4.f_fslw;
frame->f_pc = insn.is_pc;
}
if (ufetch_short((void *)(insn.is_pc), &sval)) {
DPRINTF(("%s: fault reading opcode\n", __func__));
fpe_abort(frame, ksi, SIGSEGV, SEGV_ACCERR);
}
if ((sval & 0xf000) != 0xf000) {
DPRINTF(("%s: not coproc. insn.: opcode=0x%x\n",
__func__, sval));
fpe_abort(frame, ksi, SIGILL, ILL_ILLOPC);
}
if ((sval & 0x0E00) != 0x0200) {
DPRINTF(("%s: bad coproc. id: opcode=0x%x\n", __func__, sval));
fpe_abort(frame, ksi, SIGILL, ILL_ILLOPC);
}
insn.is_opcode = sval;
optype = (sval & 0x01C0);
if (ufetch_short((void *)(insn.is_pc + 2), &sval)) {
DPRINTF(("%s: fault reading word1\n", __func__));
fpe_abort(frame, ksi, SIGSEGV, SEGV_ACCERR);
}
insn.is_word1 = sval;
insn.is_advance = 4;
DUMP_INSN(&insn);
if (optype == 0x0000) {
if ((sval & 0x8000)) {
if ((sval & 0x4000)) {
DPRINTF(("%s: fmovm FPr\n", __func__));
sig = fpu_emul_fmovm(&fe, &insn);
} else {
DPRINTF(("%s: fmovm FPcr\n", __func__));
sig = fpu_emul_fmovmcr(&fe, &insn);
}
} else {
if ((sval & 0xe000) == 0x6000) {
DPRINTF(("%s: fmove to mem\n", __func__));
sig = fpu_emul_fstore(&fe, &insn);
} else if ((sval & 0xfc00) == 0x5c00) {
DPRINTF(("%s: fmovecr\n", __func__));
sig = fpu_emul_fmovecr(&fe, &insn);
} else if ((sval & 0xa07f) == 0x26) {
DPRINTF(("%s: fscale\n", __func__));
sig = fpu_emul_fscale(&fe, &insn);
} else {
DPRINTF(("%s: other type0\n", __func__));
sig = fpu_emul_arith(&fe, &insn);
}
if (sig == 0) {
DPRINTF(("%s: type 0 returned 0\n", __func__));
sig = fpu_upd_excp(&fe);
}
}
} else if (optype == 0x0080 || optype == 0x00C0) {
DPRINTF(("%s: fbcc %s\n", __func__,
(optype & 0x40) ? "long" : "short"));
sig = fpu_emul_brcc(&fe, &insn);
} else if (optype == 0x0040) {
DPRINTF(("%s: type1\n", __func__));
sig = fpu_emul_type1(&fe, &insn);
if (sig == SIGFPE) {
ksi->ksi_trap = T_TRAPVINST;
}
} else {
DPRINTF(("%s: bad opcode type: opcode=0x%x\n", __func__,
insn.is_opcode));
sig = SIGILL;
}
DUMP_INSN(&insn);
if ((sig == 0) || (sig == SIGFPE))
frame->f_pc += insn.is_advance;
#if defined(DDB) && defined(DEBUG_FPE)
else {
printf("%s: sig=%d, opcode=%x, word1=%x\n", __func__,
sig, insn.is_opcode, insn.is_word1);
kdb_trap(-1, (db_regs_t *)&frame);
}
#endif
#if 0
if (frame->f_format == 4) {
if (insn.is_nextpc)
frame->f_pc = insn.is_nextpc;
}
#endif
DPRINTF(("%s: EXITING: w/FPSR=%08x, FPCR=%08x\n", __func__,
fe.fe_fpsr, fe.fe_fpcr));
if (sig)
fpe_abort(frame, ksi, sig, 0);
return sig;
}
int
fpu_upd_excp(struct fpemu *fe)
{
uint32_t fpsr;
uint32_t fpcr;
fpsr = fe->fe_fpsr;
fpcr = fe->fe_fpcr;
if (fpsr & (FPSR_BSUN | FPSR_SNAN | FPSR_OPERR)) {
fpsr |= FPSR_AIOP;
}
if (fpsr & FPSR_OVFL) {
fpsr |= FPSR_AOVFL;
}
if ((fpsr & FPSR_UNFL) && (fpsr & FPSR_INEX2)) {
fpsr |= FPSR_AUNFL;
}
if (fpsr & FPSR_DZ) {
fpsr |= FPSR_ADZ;
}
if (fpsr & (FPSR_INEX1 | FPSR_INEX2 | FPSR_OVFL)) {
fpsr |= FPSR_AINEX;
}
fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr;
return (fpsr & fpcr & FPSR_EXCP) ? SIGFPE : 0;
}
uint32_t
fpu_upd_fpsr(struct fpemu *fe, struct fpn *fp)
{
uint32_t fpsr;
DPRINTF(("%s: previous fpsr=%08x\n", __func__, fe->fe_fpsr));
fpsr = fe->fe_fpsr & ~FPSR_CCB;
DPRINTF(("%s: result is a ", __func__));
if (fp->fp_sign) {
DPRINTF(("negative "));
fpsr |= FPSR_NEG;
} else {
DPRINTF(("positive "));
}
switch (fp->fp_class) {
case FPC_SNAN:
DPRINTF(("signaling NAN\n"));
fpsr |= (FPSR_NAN | FPSR_SNAN);
break;
case FPC_QNAN:
DPRINTF(("quiet NAN\n"));
fpsr |= FPSR_NAN;
break;
case FPC_ZERO:
DPRINTF(("Zero\n"));
fpsr |= FPSR_ZERO;
break;
case FPC_INF:
DPRINTF(("Inf\n"));
fpsr |= FPSR_INF;
break;
default:
DPRINTF(("Number\n"));
break;
}
fe->fe_fpsr = fe->fe_fpframe->fpf_fpsr = fpsr;
DPRINTF(("%s: new fpsr=%08x\n", __func__, fe->fe_fpframe->fpf_fpsr));
return fpsr;
}
static int
fpu_emul_fmovmcr(struct fpemu *fe, struct instruction *insn)
{
struct frame *frame = fe->fe_frame;
struct fpframe *fpf = fe->fe_fpframe;
int sig;
int reglist;
int regcount;
int fpu_to_mem;
int modreg;
uint32_t tmp[3];
reglist = (insn->is_word1 & 0x1c00) >> 10;
fpu_to_mem = insn->is_word1 & 0x2000;
modreg = insn->is_opcode & 077;
if (fpu_to_mem) {
if (modreg >= 072) {
return SIGILL;
}
} else {
if (modreg >= 075) {
return SIGILL;
}
}
if (reglist == 0)
reglist = 1;
if (reglist == 7) {
regcount = 3;
} else if (reglist == 3 || reglist == 5 || reglist == 6) {
regcount = 2;
} else {
regcount = 1;
}
insn->is_datasize = regcount * 4;
sig = fpu_decode_ea(frame, insn, &insn->is_ea, modreg);
if (sig)
return sig;
if ((insn->is_ea.ea_flags & EA_DIRECT)) {
if (insn->is_ea.ea_regnum < 8) {
if (regcount != 1) {
return SIGILL;
}
} else {
if (reglist != 1) {
return SIGILL;
}
}
}
if (fpu_to_mem) {
uint32_t *s = &tmp[0];
if ((reglist & 4)) {
*s++ = fpf->fpf_fpcr;
}
if ((reglist & 2)) {
*s++ = fpf->fpf_fpsr;
}
if ((reglist & 1)) {
*s++ = fpf->fpf_fpiar;
}
sig = fpu_store_ea(frame, insn, &insn->is_ea, (char *)tmp);
} else {
const uint32_t *d = &tmp[0];
sig = fpu_load_ea(frame, insn, &insn->is_ea, (char *)tmp);
if (sig)
return sig;
if ((reglist & 4)) {
fpf->fpf_fpcr = *d++;
fpf->fpf_fpcr &= 0x0000fff0;
}
if ((reglist & 2)) {
fpf->fpf_fpsr = *d++;
fpf->fpf_fpsr &= 0x0ffffff8;
}
if ((reglist & 1)) {
fpf->fpf_fpiar = *d++;
}
}
return sig;
}
static int
fpu_emul_fmovm(struct fpemu *fe, struct instruction *insn)
{
struct frame *frame = fe->fe_frame;
struct fpframe *fpf = fe->fe_fpframe;
int word1, sig;
int reglist, regmask, regnum;
int modreg;
int fpu_to_mem, order;
int *fpregs;
insn->is_datasize = 12;
word1 = insn->is_word1;
fpu_to_mem = word1 & 0x2000;
if (word1 & 0x0800) {
reglist = frame->f_regs[(word1 & 0x70) >> 4];
} else {
reglist = word1;
}
reglist &= 0xFF;
modreg = insn->is_opcode & 077;
if (fpu_to_mem) {
if (modreg < 020 || (modreg >> 3) == 3 || modreg >= 072) {
return SIGILL;
}
} else {
if (modreg < 020 || (modreg >> 3) == 4 || modreg >= 074) {
return SIGILL;
}
}
sig = fpu_decode_ea(frame, insn, &insn->is_ea, modreg);
if (sig)
return sig;
fpregs = &fpf->fpf_regs[0];
if (insn->is_ea.ea_flags & EA_PREDECR) {
regnum = 7;
order = -1;
} else {
regnum = 0;
order = 1;
}
regmask = 0x80;
while ((0 <= regnum) && (regnum < 8)) {
if (regmask & reglist) {
if (fpu_to_mem) {
sig = fpu_store_ea(frame, insn, &insn->is_ea,
(char *)&fpregs[regnum * 3]);
DPRINTF(("%s: FP%d (%08x,%08x,%08x) saved\n",
__func__, regnum,
fpregs[regnum * 3],
fpregs[regnum * 3 + 1],
fpregs[regnum * 3 + 2]));
} else {
sig = fpu_load_ea(frame, insn, &insn->is_ea,
(char *)&fpregs[regnum * 3]);
DPRINTF(("%s: FP%d (%08x,%08x,%08x) loaded\n",
__func__, regnum,
fpregs[regnum * 3],
fpregs[regnum * 3 + 1],
fpregs[regnum * 3 + 2]));
}
if (sig)
break;
}
regnum += order;
regmask >>= 1;
}
return sig;
}
struct fpn *
fpu_cmp(struct fpemu *fe)
{
struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2;
if (x->fp_class < 0 || y->fp_class < 0) {
x->fp_class =
(y->fp_class < x->fp_class) ? y->fp_class : x->fp_class;
} else if (x->fp_class == FPC_INF) {
if (y->fp_class == FPC_INF) {
if (x->fp_sign == y->fp_sign) {
x->fp_class = FPC_ZERO;
} else {
x->fp_class = FPC_NUM;
x->fp_exp = 16383;
x->fp_mant[0] = FP_1;
}
} else {
x->fp_class = FPC_NUM;
x->fp_exp = 16383;
x->fp_mant[0] = FP_1;
}
} else if (y->fp_class == FPC_INF) {
x->fp_class = FPC_NUM;
x->fp_sign = !y->fp_sign;
x->fp_exp = 16383;
x->fp_mant[0] = FP_1;
} else {
y->fp_sign = !y->fp_sign;
x = fpu_add(fe);
if (x->fp_class == FPC_INF) {
x->fp_class = FPC_NUM;
x->fp_exp = 16383;
x->fp_mant[0] = FP_1;
}
}
return x;
}
static int
fpu_emul_arith(struct fpemu *fe, struct instruction *insn)
{
struct frame *frame = fe->fe_frame;
uint32_t *fpregs = &(fe->fe_fpframe->fpf_regs[0]);
struct fpn *res;
int word1, sig = 0;
int regnum, format;
int modreg;
int discard_result = 0;
uint32_t buf[3];
#ifdef DEBUG_FPE
int flags;
char regname;
#endif
fe->fe_fpsr &= ~FPSR_EXCP;
DUMP_INSN(insn);
DPRINTF(("%s: FPSR = %08x, FPCR = %08x\n", __func__,
fe->fe_fpsr, fe->fe_fpcr));
word1 = insn->is_word1;
format = (word1 >> 10) & 7;
regnum = (word1 >> 7) & 7;
DPRINTF(("%s: dst/src FP%d=%08x,%08x,%08x\n", __func__,
regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1],
fpregs[regnum * 3 + 2]));
fpu_explode(fe, &fe->fe_f1, FTYPE_EXT, &fpregs[regnum * 3]);
DUMP_INSN(insn);
if ((word1 & 0x4000) == 0) {
DPRINTF(("%s: FP%d op FP%d => FP%d\n", __func__,
format, regnum, regnum));
DPRINTF(("%s: src opr FP%d=%08x,%08x,%08x\n", __func__,
format, fpregs[format * 3], fpregs[format * 3 + 1],
fpregs[format * 3 + 2]));
fpu_explode(fe, &fe->fe_f2, FTYPE_EXT, &fpregs[format * 3]);
} else {
if (format == FTYPE_DBL) {
insn->is_datasize = 8;
} else if (format == FTYPE_SNG || format == FTYPE_LNG) {
insn->is_datasize = 4;
} else if (format == FTYPE_WRD) {
insn->is_datasize = 2;
} else if (format == FTYPE_BYT) {
insn->is_datasize = 1;
} else if (format == FTYPE_EXT) {
insn->is_datasize = 12;
} else {
sig = SIGFPE;
return sig;
}
modreg = insn->is_opcode & 077;
if ((modreg >> 3) == 1 || modreg >= 075) {
return SIGILL;
}
sig = fpu_decode_ea(frame, insn, &insn->is_ea, modreg);
if (sig) {
DPRINTF(("%s: error in fpu_decode_ea\n", __func__));
return sig;
}
if (insn->is_ea.ea_flags == EA_DIRECT &&
insn->is_datasize > 4) {
DPRINTF(("%s: attempted to fetch dbl/ext from reg\n",
__func__));
return SIGILL;
}
DUMP_INSN(insn);
#ifdef DEBUG_FPE
printf("%s: addr mode = ", __func__);
flags = insn->is_ea.ea_flags;
regname = (insn->is_ea.ea_regnum & 8) ? 'a' : 'd';
if (flags & EA_DIRECT) {
printf("%c%d\n", regname, insn->is_ea.ea_regnum & 7);
} else if (flags & EA_PC_REL) {
if (flags & EA_OFFSET) {
printf("pc@(%d)\n", insn->is_ea.ea_offset);
} else if (flags & EA_INDEXED) {
printf("pc@(...)\n");
}
} else if (flags & EA_PREDECR) {
printf("%c%d@-\n", regname, insn->is_ea.ea_regnum & 7);
} else if (flags & EA_POSTINCR) {
printf("%c%d@+\n", regname, insn->is_ea.ea_regnum & 7);
} else if (flags & EA_OFFSET) {
printf("%c%d@(%d)\n", regname,
insn->is_ea.ea_regnum & 7,
insn->is_ea.ea_offset);
} else if (flags & EA_INDEXED) {
printf("%c%d@(...)\n", regname,
insn->is_ea.ea_regnum & 7);
} else if (flags & EA_ABS) {
printf("0x%08x\n", insn->is_ea.ea_absaddr);
} else if (flags & EA_IMMED) {
printf("#0x%08x,%08x,%08x\n",
insn->is_ea.ea_immed[0],
insn->is_ea.ea_immed[1],
insn->is_ea.ea_immed[2]);
} else {
printf("%c%d@\n", regname, insn->is_ea.ea_regnum & 7);
}
#endif
fpu_load_ea(frame, insn, &insn->is_ea, (char*)buf);
if (format == FTYPE_WRD) {
buf[0] &= 0xffff;
if (buf[0] & 0x8000)
buf[0] |= 0xffff0000;
format = FTYPE_LNG;
} else if (format == FTYPE_BYT) {
buf[0] &= 0xff;
if (buf[0] & 0x80)
buf[0] |= 0xffffff00;
format = FTYPE_LNG;
}
DPRINTF(("%s: src = %08x %08x %08x, siz = %d\n", __func__,
buf[0], buf[1], buf[2], insn->is_datasize));
fpu_explode(fe, &fe->fe_f2, format, buf);
}
DUMP_INSN(insn);
res = NULL;
switch (word1 & 0x7f) {
case 0x00:
res = &fe->fe_f2;
break;
case 0x01:
res = fpu_int(fe);
break;
case 0x02:
res = fpu_sinh(fe);
break;
case 0x03:
res = fpu_intrz(fe);
break;
case 0x04:
res = fpu_sqrt(fe);
break;
case 0x06:
res = fpu_lognp1(fe);
break;
case 0x08:
res = fpu_etoxm1(fe);
break;
case 0x09:
res = fpu_tanh(fe);
break;
case 0x0A:
res = fpu_atan(fe);
break;
case 0x0C:
res = fpu_asin(fe);
break;
case 0x0D:
res = fpu_atanh(fe);
break;
case 0x0E:
res = fpu_sin(fe);
break;
case 0x0F:
res = fpu_tan(fe);
break;
case 0x10:
res = fpu_etox(fe);
break;
case 0x11:
res = fpu_twotox(fe);
break;
case 0x12:
res = fpu_tentox(fe);
break;
case 0x14:
res = fpu_logn(fe);
break;
case 0x15:
res = fpu_log10(fe);
break;
case 0x16:
res = fpu_log2(fe);
break;
case 0x18:
fe->fe_f2.fp_sign = 0;
res = &fe->fe_f2;
break;
case 0x19:
res = fpu_cosh(fe);
break;
case 0x1A:
fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign;
res = &fe->fe_f2;
break;
case 0x1C:
res = fpu_acos(fe);
break;
case 0x1D:
res = fpu_cos(fe);
break;
case 0x1E:
res = fpu_getexp(fe);
break;
case 0x1F:
res = fpu_getman(fe);
break;
case 0x20:
case 0x24:
res = fpu_div(fe);
break;
case 0x21:
res = fpu_mod(fe);
break;
case 0x28:
fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign;
case 0x22:
res = fpu_add(fe);
break;
case 0x23:
case 0x27:
res = fpu_mul(fe);
break;
case 0x25:
res = fpu_rem(fe);
break;
case 0x26:
break;
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
res = fpu_sincos(fe, word1 & 7);
break;
case 0x38:
res = fpu_cmp(fe);
discard_result = 1;
break;
case 0x3A:
res = &fe->fe_f2;
discard_result = 1;
break;
default:
DPRINTF(("%s: bad opcode=0x%x, word1=0x%x\n", __func__,
insn->is_opcode, insn->is_word1));
sig = SIGILL;
}
if (res == NULL)
sig = SIGILL;
if (sig == 0) {
if (!discard_result)
fpu_implode(fe, res, FTYPE_EXT, &fpregs[regnum * 3]);
fpu_upd_fpsr(fe, res);
#ifdef DEBUG_FPE
if (!discard_result) {
printf("%s: %08x,%08x,%08x stored in FP%d\n", __func__,
fpregs[regnum * 3],
fpregs[regnum * 3 + 1],
fpregs[regnum * 3 + 2],
regnum);
} else {
static const char *class_name[] =
{ "SNAN", "QNAN", "ZERO", "NUM", "INF" };
printf("%s: result(%s,%c,%d,%08x,%08x,%08x) "
"discarded\n", __func__,
class_name[res->fp_class + 2],
res->fp_sign ? '-' : '+', res->fp_exp,
res->fp_mant[0], res->fp_mant[1],
res->fp_mant[2]);
}
#endif
} else {
DPRINTF(("%s: received signal %d\n", __func__, sig));
}
DPRINTF(("%s: FPSR = %08x, FPCR = %08x\n", __func__,
fe->fe_fpsr, fe->fe_fpcr));
DUMP_INSN(insn);
return sig;
}
static int
test_cc(struct fpemu *fe, int pred)
{
int result, sig_bsun;
int fpsr;
fpsr = fe->fe_fpsr;
DPRINTF(("%s: fpsr=0x%08x\n", __func__, fpsr));
pred &= 0x3f;
DPRINTF(("%s: ", __func__));
if (pred >= 0x20) {
DPRINTF(("Illegal condition code\n"));
return SIGILL;
} else if (pred & 0x10) {
sig_bsun = 1;
pred &= 0x0f;
} else {
DPRINTF(("IEEE "));
sig_bsun = 0;
}
if ((pred & 0x08) == 0) {
result = ((fpsr & FPSR_NAN) == 0);
} else {
result = 1;
}
switch (pred & 0x06) {
case 0x00:
result &= 0;
break;
case 0x02:
result &= ((fpsr & FPSR_NEG) == 0);
break;
case 0x04:
result &= ((fpsr & FPSR_NEG) != 0);
break;
case 0x06:
result &= 1;
break;
}
if ((pred & 0x01) == 0) {
result &= ((fpsr & FPSR_ZERO) == 0);
} else {
result |= ((fpsr & FPSR_ZERO) != 0);
}
if ((pred & 0x08) != 0) {
result |= ((fpsr & FPSR_NAN) != 0);
}
DPRINTF(("=> %s (%d)\n", result ? "true" : "false", result));
if (sig_bsun && (fpsr & FPSR_NAN)) {
fpsr |= FPSR_BSUN;
}
if ((fpsr & FPSR_BSUN)) {
fpsr |= FPSR_AIOP;
}
fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr;
return -result;
}
static int
fpu_emul_type1(struct fpemu *fe, struct instruction *insn)
{
struct frame *frame = fe->fe_frame;
int advance, sig, branch, displ;
unsigned short sval;
branch = test_cc(fe, insn->is_word1);
if (branch > 0)
return branch;
fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr;
sig = 0;
switch (insn->is_opcode & 070) {
case 010:
if (branch) {
insn->is_advance = 6;
} else {
uint16_t count = frame->f_regs[insn->is_opcode & 7];
if (count-- != 0) {
if (ufetch_short((void *)(insn->is_pc +
insn->is_advance),
&sval)) {
DPRINTF(("%s: fault reading "
"displacement\n", __func__));
return SIGSEGV;
}
displ = sval;
displ &= 0xffff;
if (displ & 0x8000) {
displ |= 0xffff0000;
}
insn->is_advance += displ;
#if 0
insn->is_nextpc = insn->is_pc +
insn->is_advance;
#endif
} else {
insn->is_advance = 6;
}
frame->f_regs[insn->is_opcode & 7] &= 0xffff0000;
frame->f_regs[insn->is_opcode & 7] |= (uint32_t)count;
}
break;
case 070:
advance = 4;
if ((insn->is_opcode & 07) >= 2) {
switch (insn->is_opcode & 07) {
case 3:
advance += 2;
case 2:
advance += 2;
case 4:
break;
default:
return SIGILL;
break;
}
insn->is_advance = advance;
if (branch) {
sig = SIGFPE;
}
break;
}
default:
insn->is_datasize = 1;
sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
if (sig) {
break;
}
sig = fpu_store_ea(frame, insn, &insn->is_ea, (char *)&branch);
break;
}
return sig;
}
static int
fpu_emul_brcc(struct fpemu *fe, struct instruction *insn)
{
int displ, word2;
int sig;
unsigned short sval;
displ = insn->is_word1;
if (insn->is_opcode & 0x40) {
if (ufetch_short((void *)(insn->is_pc + insn->is_advance),
&sval)) {
DPRINTF(("%s: fault reading word2\n", __func__));
return SIGSEGV;
}
word2 = sval;
displ <<= 16;
displ |= word2;
insn->is_advance += 2;
} else {
if (displ & 0x8000)
displ |= 0xFFFF0000;
}
sig = test_cc(fe, insn->is_opcode);
fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr;
if (fe->fe_fpsr & fe->fe_fpcr & FPSR_EXCP) {
return SIGFPE;
}
if (sig == -1) {
insn->is_advance = displ + 2;
#if 0
insn->is_nextpc = insn->is_pc + insn->is_advance;
#endif
} else if (sig)
return SIGILL;
DPRINTF(("%s: %s insn @ %x (%x+%x) (disp=%x)\n", __func__,
(sig == -1) ? "BRANCH to" : "NEXT",
insn->is_pc + insn->is_advance, insn->is_pc, insn->is_advance,
displ));
return 0;
}