diff options
Diffstat (limited to 'sys/arch/m68k/fpe/fpu_emulate.c')
-rw-r--r-- | sys/arch/m68k/fpe/fpu_emulate.c | 1590 |
1 files changed, 1060 insertions, 530 deletions
diff --git a/sys/arch/m68k/fpe/fpu_emulate.c b/sys/arch/m68k/fpe/fpu_emulate.c index 7fd5cd3dfed..ed86855d09a 100644 --- a/sys/arch/m68k/fpe/fpu_emulate.c +++ b/sys/arch/m68k/fpe/fpu_emulate.c @@ -1,7 +1,8 @@ -/* $NetBSD: fpu_emulate.c,v 1.2 1995/03/10 01:43:05 gwr Exp $ */ +/* $NetBSD: fpu_emulate.c,v 1.4 1995/11/05 00:35:17 briggs Exp $ */ /* * Copyright (c) 1995 Gordon W. Ross + * some portion Copyright (c) 1995 Ken Nakata * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,153 +40,390 @@ #include <sys/signal.h> #include <machine/frame.h> -#define DEBUG 1 /* XXX */ +#include "fpu_emulate.h" + +static int fpu_emul_fmovmcr __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_fmovm __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_arith __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_type1 __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_brcc __P((struct fpemu *fe, struct instruction *insn)); +static int test_cc __P((struct fpemu *fe, int pred)); +static struct fpn *fpu_cmp __P((struct fpemu *fe)); + +#if !defined(DL_DEFAULT) +# if defined(DEBUG_WITH_FPU) +# define DL_DEFAULT DL_ALL +# else +# define DL_DEFAULT 0 +# endif +#endif -/* - * Internal info about a decoded effective address. - */ -struct insn_ea { - int regnum; - int immed; - int flags; -#define EA_DIRECT 0x01 -#define EA_PREDECR 0x02 -#define EA_POSTINCR 0x04 -#define EA_OFFSET 0x08 /* mode 5: base+offset */ -#define EA_INDEXED 0x10 /* mode 6: complicated */ -#define EA_ABS 0x20 /* mode 7: reg 0 or 1 */ -#define EA_PC_REL 0x40 /* mode 7: reg 2 or 3 */ -#define EA_IMMED 0x80 /* mode 7: reg 4 */ -}; - -struct instruction { - int advance; /* length of instruction */ - int datasize; /* byte, word, long, float, double, ... */ - int opcode; - int word1; - struct insn_ea ea0; - struct insn_ea ea1; -}; - -int fpu_emul_fmovm(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); -int fpu_emul_type0(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); -int fpu_emul_type1(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); -int fpu_emul_brcc(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); - -static int decode_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - int modreg); -static int load_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *cpureg); -static int store_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *cpureg); +int fpu_debug_level; +static int global_debug_level = DL_DEFAULT; +#define DUMP_INSN(insn) \ +if (fpu_debug_level & DL_DUMPINSN) { \ + printf(" fpu_emulate: insn={adv=%d,siz=%d,op=%04x,w1=%04x}\n", \ + (insn)->is_advance, (insn)->is_datasize, \ + (insn)->is_opcode, (insn)->is_word1); \ +} + +#ifdef DEBUG_WITH_FPU +/* mock fpframe for FPE - it's never overwritten by the real fpframe */ +struct fpframe mockfpf; +#endif /* * Emulate a floating-point instruction. * Return zero for success, else signal number. * (Typically: zero, SIGFPE, SIGILL, SIGSEGV) */ -int fpu_emulate(struct frame *frame, struct fpframe *fpf) +int +fpu_emulate(frame, fpf) + struct frame *frame; + struct fpframe *fpf; { - struct instruction insn; - int word, optype, sig; - - word = fusword(frame->f_pc); - if (word < 0) { -#ifdef DEBUG - printf("fpu_emulate: fault reading opcode\n"); + static struct instruction insn; + static struct fpemu fe; + int word, optype, sig; + int i; + u_int *pt; + +#ifdef DEBUG + /* initialize insn.is_datasize to tell it is *not* initialized */ + insn.is_datasize = -1; +#endif + fe.fe_frame = frame; +#ifdef DEBUG_WITH_FPU + fe.fe_fpframe = &mockfpf; + fe.fe_fpsr = mockfpf.fpf_fpsr; + fe.fe_fpcr = mockfpf.fpf_fpcr; +#else + fe.fe_fpframe = fpf; + fe.fe_fpsr = fpf->fpf_fpsr; + fe.fe_fpcr = fpf->fpf_fpcr; #endif - return SIGSEGV; - } - if ((word & 0xF000) != 0xF000) { -#ifdef DEBUG - printf("fpu_emulate: not coproc. insn.: opcode=0x%x\n", word); +#ifdef DEBUG + if ((fpu_debug_level = (fe.fe_fpcr >> 16) & 0x0000ffff) == 0) { + /* set the default */ + fpu_debug_level = global_debug_level; + } #endif - return SIGILL; - } - if ((word & 0x0E00) != 0x0200) { -#ifdef DEBUG - printf("fpu_emulate: bad coproc. id: opcode=0x%x\n", word); + if (fpu_debug_level & DL_VERBOSE) { + printf("ENTERING fpu_emulate: FPSR=%08x, FPCR=%08x\n", + fe.fe_fpsr, fe.fe_fpcr); + } + word = fusword(frame->f_pc); + if (word < 0) { +#ifdef DEBUG + printf(" fpu_emulate: fault reading opcode\n"); #endif - return SIGILL; - } + return SIGSEGV; + } - insn.opcode = word; - optype = (word & 0x01C0); + if ((word & 0xf000) != 0xf000) { +#ifdef DEBUG + printf(" fpu_emulate: not coproc. insn.: opcode=0x%x\n", word); +#endif + return SIGILL; + } - word = fusword(frame->f_pc + 2); - if (word < 0) { -#ifdef DEBUG - printf("fpu_emulate: fault reading word1\n"); + if ( +#ifdef DEBUG_WITH_FPU + (word & 0x0E00) != 0x0c00 /* accept fake ID == 6 */ +#else + (word & 0x0E00) != 0x0200 #endif - return SIGSEGV; - } - insn.word1 = word; + ) { +#ifdef DEBUG + printf(" fpu_emulate: bad coproc. id: opcode=0x%x\n", word); +#endif + return SIGILL; + } - /* - * Which family (or type) of opcode is it? - * Tests ordered by likelihood (hopefully). - * Certainly, type 0 is the most common. - */ - if (optype == 0x0000) { - /* type=0: generic */ - if (insn.word1 & 0x8000) { - sig = fpu_emul_fmovm(frame, fpf, &insn); - } else { - sig = fpu_emul_type0(frame, fpf, &insn); - } - } - else if (optype == 0x0080) { - /* type=2: fbcc, short disp. */ - sig = fpu_emul_brcc(frame, fpf, &insn); - } - else if (optype == 0x00C0) { - /* type=3: fbcc, long disp. */ - sig = fpu_emul_brcc(frame, fpf, &insn); - } - else if (optype == 0x0040) { - /* type=1: fdbcc, fscc, ftrapcc */ - sig = fpu_emul_type1(frame, fpf, &insn); - } - else { - /* type=4: fsave (privileged) */ - /* type=5: frestore (privileged) */ - /* type=6: reserved */ - /* type=7: reserved */ -#ifdef DEBUG - printf("fpu_emulate: bad opcode type: opcode=0x%x\n", insn.opcode); + insn.is_opcode = word; + optype = (word & 0x01C0); + + word = fusword(frame->f_pc + 2); + if (word < 0) { +#ifdef DEBUG + printf(" fpu_emulate: fault reading word1\n"); #endif - sig = SIGILL; + return SIGSEGV; + } + insn.is_word1 = word; + /* all FPU instructions are at least 4-byte long */ + insn.is_advance = 4; + + DUMP_INSN(&insn); + + /* + * Which family (or type) of opcode is it? + * Tests ordered by likelihood (hopefully). + * Certainly, type 0 is the most common. + */ + if (optype == 0x0000) { + /* type=0: generic */ + if ((word & 0xc000) == 0xc000) { + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmovm FPr\n"); + sig = fpu_emul_fmovm(&fe, &insn); + } else if ((word & 0xc000) == 0x8000) { + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmovm FPcr\n"); + sig = fpu_emul_fmovmcr(&fe, &insn); + } else if ((word & 0xe000) == 0x6000) { + /* fstore = fmove FPn,mem */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmove to mem\n"); + sig = fpu_emul_fstore(&fe, &insn); + } else if ((word & 0xfc00) == 0x5c00) { + /* fmovecr */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmovecr\n"); + sig = fpu_emul_fmovecr(&fe, &insn); + } else if ((word & 0xa07f) == 0x26) { + /* fscale */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fscale\n"); + sig = fpu_emul_fscale(&fe, &insn); + } else { + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulte: other type0\n"); + /* all other type0 insns are arithmetic */ + sig = fpu_emul_arith(&fe, &insn); } - if (sig == 0) { - frame->f_pc += insn.advance; + if (fpu_debug_level & DL_VERBOSE) + printf(" fpu_emulate: type 0 returned 0\n"); + sig = fpu_upd_excp(&fe); } + } else if (optype == 0x0080 || optype == 0x00C0) { + /* type=2 or 3: fbcc, short or long disp. */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fbcc %s\n", + (optype & 0x40) ? "long" : "short"); + sig = fpu_emul_brcc(&fe, &insn); + } else if (optype == 0x0040) { + /* type=1: fdbcc, fscc, ftrapcc */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: type1\n"); + sig = fpu_emul_type1(&fe, &insn); + } else { + /* type=4: fsave (privileged) */ + /* type=5: frestore (privileged) */ + /* type=6: reserved */ + /* type=7: reserved */ +#ifdef DEBUG + printf(" fpu_emulate: bad opcode type: opcode=0x%x\n", insn.is_opcode); +#endif + sig = SIGILL; + } + + DUMP_INSN(&insn); + + if (sig == 0) { + frame->f_pc += insn.is_advance; + } #if defined(DDB) && defined(DEBUG) - else kdb_trap(-1, frame); + else { + printf(" fpu_emulate: sig=%d, opcode=%x, word1=%x\n", + sig, insn.is_opcode, insn.is_word1); + kdb_trap(-1, frame); + } #endif - return (sig); + if (fpu_debug_level & DL_VERBOSE) + printf("EXITING fpu_emulate: w/FPSR=%08x, FPCR=%08x\n", + fe.fe_fpsr, fe.fe_fpcr); + + return (sig); +} + +/* update accrued exception bits and see if there's an FP exception */ +int +fpu_upd_excp(fe) + struct fpemu *fe; +{ + u_int fpsr; + u_int fpcr; + + fpsr = fe->fe_fpsr; + fpcr = fe->fe_fpcr; + /* update fpsr accrued exception bits; each insn doesn't have to + update this */ + 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; +} + +/* update fpsr according to fp (= result of an fp op) */ +u_int +fpu_upd_fpsr(fe, fp) + struct fpemu *fe; + struct fpn *fp; +{ + u_int fpsr; + + if (fpu_debug_level & DL_RESULT) + printf(" fpu_upd_fpsr: previous fpsr=%08x\n", fe->fe_fpsr); + + /* clear all condition code */ + fpsr = fe->fe_fpsr & ~FPSR_CCB; + + if (fpu_debug_level & DL_RESULT) + printf(" fpu_upd_fpsr: result is a "); + + if (fp->fp_sign) { + if (fpu_debug_level & DL_RESULT) + printf("negative "); + fpsr |= FPSR_NEG; + } else { + if (fpu_debug_level & DL_RESULT) + printf("positive "); + } + + switch (fp->fp_class) { + case FPC_SNAN: + if (fpu_debug_level & DL_RESULT) + printf("signaling NAN\n"); + fpsr |= (FPSR_NAN | FPSR_SNAN); + break; + case FPC_QNAN: + if (fpu_debug_level & DL_RESULT) + printf("quiet NAN\n"); + fpsr |= FPSR_NAN; + break; + case FPC_ZERO: + if (fpu_debug_level & DL_RESULT) + printf("Zero\n"); + fpsr |= FPSR_ZERO; + break; + case FPC_INF: + if (fpu_debug_level & DL_RESULT) + printf("Inf\n"); + fpsr |= FPSR_INF; + break; + default: + if (fpu_debug_level & DL_RESULT) + printf("Number\n"); + /* anything else is treated as if it is a number */ + break; + } + + fe->fe_fpsr = fe->fe_fpframe->fpf_fpsr = fpsr; + + if (fpu_debug_level & DL_RESULT) + printf(" fpu_upd_fpsr: new fpsr=%08x\n", fe->fe_fpframe->fpf_fpsr); + + return fpsr; +} + +static int +fpu_emul_fmovmcr(fe, insn) + 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 fpu_to_mem; + + /* move to/from control registers */ + reglist = (insn->is_word1 & 0x1c00) >> 10; + /* Bit 13 selects direction (FPU to/from Mem) */ + fpu_to_mem = insn->is_word1 & 0x2000; + + insn->is_datasize = 4; + insn->is_advance = 4; + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { return sig; } + + if (reglist != 1 && reglist != 2 && reglist != 4 && + (insn->is_ea0.ea_flags & EA_DIRECT)) { + /* attempted to copy more than one FPcr to CPU regs */ +#ifdef DEBUG + printf(" fpu_emul_fmovmcr: tried to copy too many FPcr\n"); +#endif + return SIGILL; + } + + if (reglist & 4) { + /* fpcr */ + if ((insn->is_ea0.ea_flags & EA_DIRECT) && + insn->is_ea0.ea_regnum >= 8 /* address reg */) { + /* attempted to copy FPCR to An */ +#ifdef DEBUG + printf(" fpu_emul_fmovmcr: tried to copy FPCR from/to A%d\n", + insn->is_ea0.ea_regnum & 7); +#endif + return SIGILL; + } + if (fpu_to_mem) { + sig = fpu_store_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpcr); + } else { + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpcr); + } + } + if (sig) { return sig; } + + if (reglist & 2) { + /* fpsr */ + if ((insn->is_ea0.ea_flags & EA_DIRECT) && + insn->is_ea0.ea_regnum >= 8 /* address reg */) { + /* attempted to copy FPSR to An */ +#ifdef DEBUG + printf(" fpu_emul_fmovmcr: tried to copy FPSR from/to A%d\n", + insn->is_ea0.ea_regnum & 7); +#endif + return SIGILL; + } + if (fpu_to_mem) { + sig = fpu_store_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpsr); + } else { + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpsr); + } + } + if (sig) { return sig; } + + if (reglist & 1) { + /* fpiar - can be moved to/from An */ + if (fpu_to_mem) { + sig = fpu_store_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpiar); + } else { + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpiar); + } + } + return sig; } /* - * type 0: fmovem, fmove <cr> + * type 0: fmovem * Separated out of fpu_emul_type0 for efficiency. * In this function, we know: * (opcode & 0x01C0) == 0 @@ -194,455 +432,747 @@ int fpu_emulate(struct frame *frame, struct fpframe *fpf) * No conversion or rounding is done by this instruction, * and the FPSR is not affected. */ -int fpu_emul_fmovm(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) +static int +fpu_emul_fmovm(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int word1, sig; - int reglist, regmask, regnum; - int fpu_to_mem, order; - int w1_post_incr; /* XXX - FP regs order? */ - int *fpregs; - - insn->advance = 4; - insn->datasize = 12; - word1 = insn->word1; - - /* Bit 14 selects FPn or FP control regs. */ - if (word1 & 0x4000) { - /* - * Bits 12,11 select register list mode: - * 0,0: Static reg list, pre-decr. - * 0,1: Dynamic reg list, pre-decr. - * 1,0: Static reg list, post-incr. - * 1,1: Dynamic reg list, post-incr - */ - w1_post_incr = word1 & 0x1000; - if (word1 & 0x0800) { - /* dynamic reg list */ - reglist = frame->f_regs[(word1 & 0x70) >> 4]; - } else - reglist = word1; - reglist &= 0xFF; - } else { - /* XXX: move to/from control registers */ - reglist = word1 & 0x1C00; - return SIGILL; + struct frame *frame = fe->fe_frame; + struct fpframe *fpf = fe->fe_fpframe; + int word1, sig; + int reglist, regmask, regnum; + int fpu_to_mem, order; + int w1_post_incr; /* XXX - FP regs order? */ + int *fpregs; + + insn->is_advance = 4; + insn->is_datasize = 12; + word1 = insn->is_word1; + + /* Bit 13 selects direction (FPU to/from Mem) */ + fpu_to_mem = word1 & 0x2000; + + /* + * Bits 12,11 select register list mode: + * 0,0: Static reg list, pre-decr. + * 0,1: Dynamic reg list, pre-decr. + * 1,0: Static reg list, post-incr. + * 1,1: Dynamic reg list, post-incr + */ + w1_post_incr = word1 & 0x1000; + if (word1 & 0x0800) { + /* dynamic reg list */ + reglist = frame->f_regs[(word1 & 0x70) >> 4]; + } else { + reglist = word1; + } + reglist &= 0xFF; + + /* Get effective address. (modreg=opcode&077) */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { return sig; } + + /* Get address of soft coprocessor regs. */ + fpregs = &fpf->fpf_regs[0]; + + if (insn->is_ea0.ea_flags & EA_PREDECR) { + regnum = 7; + order = -1; + } else { + regnum = 0; + order = 1; + } + + while ((0 <= regnum) && (regnum < 8)) { + regmask = 1 << regnum; + if (regmask & reglist) { + if (fpu_to_mem) { + sig = fpu_store_ea(frame, insn, &insn->is_ea0, + (char*)&fpregs[regnum * 3]); + if (fpu_debug_level & DL_RESULT) + printf(" fpu_emul_fmovm: FP%d (%08x,%08x,%08x) saved\n", + regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1], + fpregs[regnum * 3 + 2]); + } else { /* mem to fpu */ + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char*)&fpregs[regnum * 3]); + if (fpu_debug_level & DL_RESULT) + printf(" fpu_emul_fmovm: FP%d (%08x,%08x,%08x) loaded\n", + regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1], + fpregs[regnum * 3 + 2]); + } + if (sig) { break; } } + regnum += order; + } - /* Bit 13 selects direction (FPU to/from Mem) */ - fpu_to_mem = word1 & 0x2000; - - /* Get effective address. (modreg=opcode&077) */ - sig = decode_ea(frame, insn, &insn->ea0, insn->opcode); - if (sig) return sig; - - /* Get address of soft coprocessor regs. */ - fpregs = &fpf->fpf_regs[0]; + return sig; +} - if (insn->ea0.flags & EA_PREDECR) { - regnum = 7; - order = -1; +static struct fpn * +fpu_cmp(fe) + struct fpemu *fe; +{ + struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2; + + /* take care of special cases */ + if (x->fp_class < 0 || y->fp_class < 0) { + /* if either of two is a SNAN, result is SNAN */ + 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) { + /* both infinities */ + if (x->fp_sign == y->fp_sign) { + x->fp_class = FPC_ZERO; /* return a signed zero */ + } else { + x->fp_class = FPC_NUM; /* return a faked number w/x's sign */ + x->fp_exp = 16383; + x->fp_mant[0] = FP_1; + } } else { - regnum = 0; - order = 1; + /* y is a number */ + x->fp_class = FPC_NUM; /* return a forged number w/x's sign */ + x->fp_exp = 16383; + x->fp_mant[0] = FP_1; } - - while ((0 <= regnum) && (regnum < 8)) { - regmask = 1 << regnum; - if (regmask & reglist) { - if (fpu_to_mem) - sig = store_ea(frame, insn, &insn->ea0, - (char*) &fpregs[regnum]); - else /* mem to fpu */ - sig = load_ea(frame, insn, &insn->ea0, - (char*) &fpregs[regnum]); - if (sig) break; - } - regnum += order; + } else if (y->fp_class == FPC_INF) { + /* x is a Num but y is an Inf */ + /* return a forged number w/y's sign inverted */ + x->fp_class = FPC_NUM; + x->fp_sign = !y->fp_sign; + x->fp_exp = 16383; + x->fp_mant[0] = FP_1; + } else { + /* x and y are both numbers or zeros, or pair of a number and a zero */ + y->fp_sign = !y->fp_sign; + x = fpu_add(fe); /* (x - y) */ + /* + * FCMP does not set Inf bit in CC, so return a forged number + * (value doesn't matter) if Inf is the result of fsub. + */ + if (x->fp_class == FPC_INF) { + x->fp_class = FPC_NUM; + x->fp_exp = 16383; + x->fp_mant[0] = FP_1; } - - return 0; -} - -int fpu_emul_type0(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) -{ - int sig; - - /* Get effective address */ - /* XXX */ - - switch(insn->word1 & 0x3F) { - - case 0x00: /* fmove */ - - case 0x01: /* fint */ - case 0x02: /* fsinh */ - case 0x03: /* fintrz */ - case 0x04: /* fsqrt */ - case 0x06: /* flognp1 */ - - case 0x09: /* ftanh */ - case 0x0A: /* fatan */ - case 0x0C: /* fasin */ - case 0x0D: /* fatanh */ - case 0x0E: /* fsin */ - case 0x0F: /* ftan */ - - case 0x10: /* fetox */ - case 0x11: /* ftwotox */ - case 0x12: /* ftentox */ - case 0x14: /* flogn */ - case 0x15: /* flog10 */ - case 0x16: /* flog2 */ - - case 0x18: /* fabs */ - case 0x19: /* fcosh */ - case 0x1A: /* fneg */ - case 0x1C: /* facos */ - case 0x1D: /* fcos */ - case 0x1E: /* fgetexp */ - case 0x1F: /* fgetman */ - - case 0x20: /* fdiv */ - case 0x21: /* fmod */ - case 0x22: /* fadd */ - case 0x23: /* fmul */ - case 0x24: /* fsgldiv */ - case 0x25: /* frem */ - case 0x26: /* fscale */ - case 0x27: /* fsglmul */ - - case 0x28: /* fsub */ - case 0x38: /* fcmp */ - case 0x3A: /* ftst */ - - default: -#ifdef DEBUG - printf("fpu_emul_type0: unknown: opcode=0x%x, word1=0x%x\n", - insn->opcode, insn->word1); -#endif - sig = SIGILL; - - } /* switch */ - return (sig); + } + return x; } /* - * type 1: fdbcc, fscc, ftrapcc - * In this function, we know: - * (opcode & 0x01C0) == 0x0040 + * arithmetic oprations */ -int fpu_emul_type1(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) +static int +fpu_emul_arith(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int sig; - - /* Get effective address */ - /* XXX */ - - switch (insn->opcode & 070) { + struct frame *frame = fe->fe_frame; + u_int *fpregs = &(fe->fe_fpframe->fpf_regs[0]); + struct fpn *res; + int word1, sig = 0; + int regnum, format; + int discard_result = 0; + u_int buf[3]; + int flags; + char regname; + + DUMP_INSN(insn); + + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: FPSR = %08x, FPCR = %08x\n", + fe->fe_fpsr, fe->fe_fpcr); + } + + word1 = insn->is_word1; + format = (word1 >> 10) & 7; + regnum = (word1 >> 7) & 7; + + /* fetch a source operand : may not be used */ + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: dst/src FP%d=%08x,%08x,%08x\n", + 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); + + /* get the other operand which is always the source */ + if ((word1 & 0x4000) == 0) { + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: FP%d op FP%d => FP%d\n", + format, regnum, regnum); + printf(" fpu_emul_arith: src opr FP%d=%08x,%08x,%08x\n", + format, fpregs[format*3], fpregs[format*3+1], + fpregs[format*3+2]); + } + fpu_explode(fe, &fe->fe_f2, FTYPE_EXT, &fpregs[format * 3]); + } else { + /* the operand is in memory */ + 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 { + /* invalid or unsupported operand format */ + sig = SIGFPE; + return sig; + } - case 010: /* fdbcc */ - /* XXX: If not CC { Decrement Dn; if (Dn >= 0) branch; } */ + /* Get effective address. (modreg=opcode&077) */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: error in fpu_decode_ea\n"); + } + return sig; + } - case 070: /* fscc or ftrapcc */ - if ((insn->opcode & 07) > 1) { - /* ftrapcc */ - /* XXX: If CC, advance and return SIGFPE */ - break; + DUMP_INSN(insn); + + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: addr mode = "); + flags = insn->is_ea0.ea_flags; + regname = (insn->is_ea0.ea_regnum & 8) ? 'a' : 'd'; + + if (flags & EA_DIRECT) { + printf("%c%d\n", + regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_PC_REL) { + if (flags & EA_OFFSET) { + printf("pc@(%d)\n", insn->is_ea0.ea_offset); + } else if (flags & EA_INDEXED) { + printf("pc@(...)\n"); } - /* fallthrough */ - default: /* fscc */ - /* XXX: If CC, store ones, else store zero */ - sig = SIGILL; - break; - + } else if (flags & EA_PREDECR) { + printf("%c%d@-\n", + regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_POSTINCR) { + printf("%c%d@+\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_OFFSET) { + printf("%c%d@(%d)\n", regname, insn->is_ea0.ea_regnum & 7, + insn->is_ea0.ea_offset); + } else if (flags & EA_INDEXED) { + printf("%c%d@(...)\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_ABS) { + printf("0x%08x\n", insn->is_ea0.ea_absaddr); + } else if (flags & EA_IMMED) { + + printf("#0x%08x,%08x,%08x\n", insn->is_ea0.ea_immed[0], + insn->is_ea0.ea_immed[1], insn->is_ea0.ea_immed[2]); + } else { + printf("%c%d@\n", regname, insn->is_ea0.ea_regnum & 7); + } + } /* if (fpu_debug_level & DL_ARITH) */ + + fpu_load_ea(frame, insn, &insn->is_ea0, (char*)buf); + if (format == FTYPE_WRD) { + /* sign-extend */ + buf[0] &= 0xffff; + if (buf[0] & 0x8000) { + buf[0] |= 0xffff0000; + } + format = FTYPE_LNG; + } else if (format == FTYPE_BYT) { + /* sign-extend */ + buf[0] &= 0xff; + if (buf[0] & 0x80) { + buf[0] |= 0xffffff00; + } + format = FTYPE_LNG; } - return (sig); -} - -/* - * Type 2 or 3: fbcc (also fnop) - * In this function, we know: - * (opcode & 0x0180) == 0x0080 - */ -int fpu_emul_brcc(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) -{ - int displ, word2; - int sig, advance; - - /* - * Get branch displacement. - */ - advance = 4; - displ = insn->word1; - if (displ & 0x8000) - displ |= 0xFFFF0000; - - if (insn->opcode & 0x40) { - word2 = fusword(frame->f_pc + 4); - if (word2 < 0) { -#ifdef DEBUG - printf("fpu_emul_brcc: fault reading word2\n"); + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: src = %08x %08x %08x, siz = %d\n", + buf[0], buf[1], buf[2], insn->is_datasize); + } + fpu_explode(fe, &fe->fe_f2, format, buf); + } + + DUMP_INSN(insn); + + /* An arithmetic instruction emulate function has a prototype of + * struct fpn *fpu_op(struct fpemu *); + + * 1) If the instruction is monadic, then fpu_op() must use + * fe->fe_f2 as its operand, and return a pointer to the + * result. + + * 2) If the instruction is diadic, then fpu_op() must use + * fe->fe_f1 and fe->fe_f2 as its two operands, and return a + * pointer to the result. + + */ + switch (word1 & 0x3f) { + case 0x00: /* fmove */ + res = &fe->fe_f2; + break; + + case 0x01: /* fint */ + res = fpu_int(fe); + break; + + case 0x02: /* fsinh */ + res = fpu_sinh(fe); + break; + + case 0x03: /* fintrz */ + res = fpu_intrz(fe); + break; + + case 0x04: /* fsqrt */ + res = fpu_sqrt(fe); + break; + + case 0x06: /* flognp1 */ + res = fpu_lognp1(fe); + break; + + case 0x08: /* fetoxm1 */ + res = fpu_etoxm1(fe); + break; + + case 0x09: /* ftanh */ + res = fpu_tanh(fe); + break; + + case 0x0A: /* fatan */ + res = fpu_atan(fe); + break; + + case 0x0C: /* fasin */ + res = fpu_asin(fe); + break; + + case 0x0D: /* fatanh */ + res = fpu_atanh(fe); + break; + + case 0x0E: /* fsin */ + res = fpu_sin(fe); + break; + + case 0x0F: /* ftan */ + res = fpu_tan(fe); + break; + + case 0x10: /* fetox */ + res = fpu_etox(fe); + break; + + case 0x11: /* ftwotox */ + res = fpu_twotox(fe); + break; + + case 0x12: /* ftentox */ + res = fpu_tentox(fe); + break; + + case 0x14: /* flogn */ + res = fpu_logn(fe); + break; + + case 0x15: /* flog10 */ + res = fpu_log10(fe); + break; + + case 0x16: /* flog2 */ + res = fpu_log2(fe); + break; + + case 0x18: /* fabs */ + fe->fe_f2.fp_sign = 0; + res = &fe->fe_f2; + break; + + case 0x19: /* fcosh */ + res = fpu_cosh(fe); + break; + + case 0x1A: /* fneg */ + fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign; + res = &fe->fe_f2; + break; + + case 0x1C: /* facos */ + res = fpu_acos(fe); + break; + + case 0x1D: /* fcos */ + res = fpu_cos(fe); + break; + + case 0x1E: /* fgetexp */ + res = fpu_getexp(fe); + break; + + case 0x1F: /* fgetman */ + res = fpu_getman(fe); + break; + + case 0x20: /* fdiv */ + case 0x24: /* fsgldiv: cheating - better than nothing */ + res = fpu_div(fe); + break; + + case 0x21: /* fmod */ + res = fpu_mod(fe); + break; + + case 0x28: /* fsub */ + fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign; /* f2 = -f2 */ + case 0x22: /* fadd */ + res = fpu_add(fe); + break; + + case 0x23: /* fmul */ + case 0x27: /* fsglmul: cheating - better than nothing */ + res = fpu_mul(fe); + break; + + case 0x25: /* frem */ + res = fpu_rem(fe); + break; + + case 0x26: + /* fscale is handled by a separate function */ + break; + + case 0x30: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: /* fsincos */ + res = fpu_sincos(fe, word1 & 7); + break; + + case 0x38: /* fcmp */ + res = fpu_cmp(fe); + discard_result = 1; + break; + + case 0x3A: /* ftst */ + res = &fe->fe_f2; + discard_result = 1; + break; + + default: +#ifdef DEBUG + printf(" fpu_emul_arith: bad opcode=0x%x, word1=0x%x\n", + insn->is_opcode, insn->is_word1); #endif - return SIGSEGV; - } - displ << 16; - displ |= word2; - advance += 2; + sig = SIGILL; + } /* switch (word1 & 0x3f) */ + + if (!discard_result && sig == 0) { + fpu_implode(fe, res, FTYPE_EXT, &fpregs[regnum * 3]); + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: %08x,%08x,%08x stored in FP%d\n", + fpregs[regnum*3], fpregs[regnum*3+1], + fpregs[regnum*3+2], regnum); } - - /* XXX: If CC, frame->f_pc += displ */ - return SIGILL; + } else if (sig == 0 && fpu_debug_level & DL_ARITH) { + static char *class_name[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" }; + printf(" fpu_emul_arith: result(%s,%c,%d,%08x,%08x,%08x,%08x) discarded\n", + class_name[res->fp_class + 2], + res->fp_sign ? '-' : '+', res->fp_exp, + res->fp_mant[0], res->fp_mant[1], + res->fp_mant[2], res->fp_mant[3]); + } else if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: received signal %d\n", sig); + } + + /* update fpsr according to the result of operation */ + fpu_upd_fpsr(fe, res); + + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: FPSR = %08x, FPCR = %08x\n", + fe->fe_fpsr, fe->fe_fpcr); + } + + DUMP_INSN(insn); + + return sig; } -/* - * Helper routines for dealing with "effective address" values. - */ - -/* - * Decode an effective address into internal form. - * Returns zero on success, else signal number. +/* test condition code according to the predicate in the opcode. + * returns -1 when the predicate evaluates to true, 0 when false. + * signal numbers are returned when an error is detected. */ -static int decode_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - int modreg) +static int +test_cc(fe, pred) + struct fpemu *fe; + int pred; { - int immed_bytes = 0; - int data; - - /* Set the most common value here. */ - ea->regnum = 8 + (modreg & 7); - - switch (modreg & 070) { - - case 0: /* Dn */ - ea->regnum = (modreg & 7); - ea->flags = EA_DIRECT; - break; - - case 010: /* An */ - ea->flags = EA_DIRECT; - break; - - case 020: /* (An) */ - ea->flags = 0; - break; - - case 030: /* (An)+ */ - ea->flags = EA_POSTINCR; - break; - - case 040: /* -(An) */ - ea->flags = EA_PREDECR; - break; - - case 050: /* (d16,An) */ - ea->flags = EA_OFFSET; - immed_bytes = 2; - break; - - case 060: /* (d8,An,Xn) */ - ea->flags = EA_INDEXED; - immed_bytes = 2; - break; + int result, sig_bsun, invert; + int fpsr; + + fpsr = fe->fe_fpsr; + invert = 0; + fpsr &= ~FPSR_EXCP; /* clear all exceptions */ + if (fpu_debug_level & DL_TESTCC) { + printf(" test_cc: fpsr=0x%08x\n", fpsr); + } + pred &= 0x3f; /* lowest 6 bits */ + + if (fpu_debug_level & DL_TESTCC) { + printf(" test_cc: "); + } + + if (pred >= 040) { + return SIGILL; + } else if (pred & 0x10) { + /* IEEE nonaware tests */ + sig_bsun = 1; + pred &= 017; /* lower 4 bits */ + } else { + /* IEEE aware tests */ + if (fpu_debug_level & DL_TESTCC) { + printf("IEEE "); + } + sig_bsun = 0; + } - case 070: /* misc. */ - ea->regnum = (modreg & 7); - switch (modreg & 7) { - - case 0: /* (xxxx).W */ - ea->flags = EA_ABS; - immed_bytes = 2; - break; - - case 1: /* (xxxxxxxx).L */ - ea->flags = EA_ABS; - immed_bytes = 4; - break; - - case 2: /* (d16,PC) */ - ea->flags = EA_PC_REL | EA_OFFSET; - immed_bytes = 2; - break; - - case 3: /* (d8,PC,Xn) */ - ea->flags = EA_PC_REL | EA_INDEXED; - immed_bytes = 2; - break; - - case 4: /* #data */ - ea->flags = EA_IMMED; - immed_bytes = insn->datasize; - break; - - default: - return SIGILL; - } /* switch for mode 7 */ - break; - } /* switch mode */ - - /* Now fetch any immediate data and advance. */ - if (immed_bytes > 0) { - data = fusword(frame->f_pc + insn->advance); - if (data < 0) - return SIGSEGV; - insn->advance += 2; - if (data & 0x8000) - data |= 0xFFFF0000; - ea->immed = data; + if (pred >= 010) { + if (fpu_debug_level & DL_TESTCC) { + printf("Not "); + } + /* predicate is "NOT ..." */ + pred ^= 0xf; /* invert */ + invert = -1; + } + switch (pred) { + case 0: /* (Signaling) False */ + if (fpu_debug_level & DL_TESTCC) { + printf("False"); + } + result = 0; + break; + case 1: /* (Signaling) Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("Equal"); + } + result = -((fpsr & FPSR_ZERO) == FPSR_ZERO); + break; + case 2: /* Greater Than */ + if (fpu_debug_level & DL_TESTCC) { + printf("GT"); + } + result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == 0); + break; + case 3: /* Greater or Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("GE"); + } + result = -((fpsr & FPSR_ZERO) || + (fpsr & (FPSR_NAN|FPSR_NEG)) == 0); + break; + case 4: /* Less Than */ + if (fpu_debug_level & DL_TESTCC) { + printf("LT"); } - if (immed_bytes > 2) { - data = fusword(frame->f_pc + insn->advance); - if (data < 0) - return SIGSEGV; - insn->advance += 2; - ea->immed <<= 16; - ea->immed |= data; + result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == FPSR_NEG); + break; + case 5: /* Less or Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("LE"); } - return 0; + result = -((fpsr & FPSR_ZERO) || + ((fpsr & (FPSR_NAN|FPSR_NEG)) == FPSR_NEG)); + break; + case 6: /* Greater or Less than */ + if (fpu_debug_level & DL_TESTCC) { + printf("GLT"); + } + result = -((fpsr & (FPSR_NAN|FPSR_ZERO)) == 0); + break; + case 7: /* Greater, Less or Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("GLE"); + } + result = -((fpsr & FPSR_NAN) == 0); + break; + default: + /* invalid predicate */ + return SIGILL; + } + result ^= invert; /* if the predicate is "NOT ...", then + invert the result */ + if (fpu_debug_level & DL_TESTCC) { + printf(" => %s (%d)\n", result ? "true" : "false", result); + } + /* if it's an IEEE unaware test and NAN is set, BSUN is set */ + if (sig_bsun && (fpsr & FPSR_NAN)) { + fpsr |= FPSR_BSUN; + } + + /* put fpsr back */ + fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr; + + return result; } - /* - * Load a value from an effective address. - * Returns zero on success, else signal number. + * type 1: fdbcc, fscc, ftrapcc + * In this function, we know: + * (opcode & 0x01C0) == 0x0040 */ -static int load_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *dst) +static int +fpu_emul_type1(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int *reg; - char *src; - int len; - -#ifdef DIAGNOSTIC - if (ea->regnum & ~0xF) - panic("load_ea: bad regnum"); + struct frame *frame = fe->fe_frame; + struct fpframe *fpf = fe->fe_fpframe; + int advance, sig, branch, displ; + + branch = test_cc(fe, insn->is_word1); + fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr; + + insn->is_advance = 4; + sig = 0; + + switch (insn->is_opcode & 070) { + case 010: /* fdbcc */ + if (branch == -1) { + /* advance */ + insn->is_advance = 6; + } else if (!branch) { + /* decrement Dn and if (Dn != -1) branch */ + u_int16_t count = frame->f_regs[insn->is_opcode & 7]; + + if (count-- != 0) { + displ = fusword(frame->f_pc + insn->is_advance); + if (displ < 0) { +#ifdef DEBUG + printf(" fpu_emul_type1: fault reading displacement\n"); #endif + return SIGSEGV; + } + /* sign-extend the displacement */ + displ &= 0xffff; + if (displ & 0x8000) { + displ |= 0xffff0000; + } + insn->is_advance += displ; + } else { + insn->is_advance = 6; + } + /* write it back */ + frame->f_regs[insn->is_opcode & 7] &= 0xffff0000; + frame->f_regs[insn->is_opcode & 7] |= (u_int32_t)count; + } else { /* got a signal */ + sig = SIGFPE; + } + break; - /* The dst is always int or larger. */ - len = insn->datasize; - if (len < 4) - dst += (4 - len); - - /* point to the register */ - if (ea->flags & EA_PC_REL) - reg = &frame->f_pc; - else - reg = &frame->f_regs[ea->regnum]; - - if (ea->flags & (EA_DIRECT | EA_IMMED)) { - if (ea->flags & EA_DIRECT) - src = (char*) reg; - if (ea->flags & EA_IMMED) - src = (char*) &ea->immed; - if (len > 4) - return SIGILL; - /* The source is an int. */ - if (len < 4) - src += (4 - len); - bcopy(src, dst, len); + case 070: /* ftrapcc or fscc */ + advance = 4; + if ((insn->is_opcode & 07) >= 2) { + switch (insn->is_opcode & 07) { + case 3: /* long opr */ + advance += 2; + case 2: /* word opr */ + advance += 2; + case 4: /* no opr */ + break; + default: + return SIGILL; + break; + } + + if (branch == 0) { + /* no trap */ + insn->is_advance = advance; + sig = 0; + } else { + /* trap */ + sig = SIGFPE; + } + break; + } /* if ((insn->is_opcode & 7) < 2), fall through to FScc */ + + default: /* fscc */ + insn->is_advance = 4; + insn->is_datasize = 1; /* always byte */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { + break; + } + if (branch == -1 || branch == 0) { + /* set result */ + sig = fpu_store_ea(frame, insn, &insn->is_ea0, (char *)&branch); } else { - /* One of MANY indirect forms... */ - - /* do pre-decrement */ - if (ea->flags & EA_PREDECR) - *reg -= len; - - /* Grab the register contents. */ - src = (char*) *reg; - - /* apply the signed offset */ - if (ea->flags & EA_OFFSET) - src += ea->immed; - - /* XXX - Don't know how to handle this yet. */ - if (ea->flags & EA_INDEXED) - return SIGILL; - - copyin(src, dst, len); - - /* do post-increment */ - if (ea->flags & EA_POSTINCR) - *reg += len; + /* got an exception */ + sig = branch; } - - return 0; + break; + } + return sig; } /* - * Store a value at the effective address. - * Returns zero on success, else signal number. + * Type 2 or 3: fbcc (also fnop) + * In this function, we know: + * (opcode & 0x0180) == 0x0080 */ -static int store_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *src) +static int +fpu_emul_brcc(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int *reg; - char *dst; - int len; - -#ifdef DIAGNOSTIC - if (ea->regnum & ~0xF) - panic("load_ea: bad regnum"); + struct frame *frame = fe->fe_frame; + struct fpframe *fpf = fe->fe_fpframe; + int displ, word2; + int sig, advance; + + /* + * Get branch displacement. + */ + insn->is_advance = 4; + displ = insn->is_word1; + + if (insn->is_opcode & 0x40) { + word2 = fusword(frame->f_pc + insn->is_advance); + if (word2 < 0) { +#ifdef DEBUG + printf(" fpu_emul_brcc: fault reading word2\n"); #endif - - /* The src is always int or larger. */ - len = insn->datasize; - if (len < 4) - src += (4 - len); - - /* point to the register */ - if (ea->flags & EA_PC_REL) - reg = &frame->f_pc; - else - reg = &frame->f_regs[ea->regnum]; - - if (ea->flags & EA_IMMED) - return SIGILL; - - if (ea->flags & EA_DIRECT) { - dst = (char*) reg; - if (len > 4) - return SIGILL; - /* The destination is an int. */ - if (len < 4) - dst += (4 - len); - bcopy(src, dst, len); - } else { - /* One of MANY indirect forms... */ - - /* do pre-decrement */ - if (ea->flags & EA_PREDECR) - *reg -= len; - - /* Grab the register contents. */ - dst = (char*) *reg; - - /* apply the signed offset */ - if (ea->flags & EA_OFFSET) - dst += ea->immed; - - /* XXX - Don't know how to handle this yet. */ - if (ea->flags & EA_INDEXED) - return SIGILL; - - copyout(src, dst, len); - - /* do post-increment */ - if (ea->flags & EA_POSTINCR) - *reg += len; + return SIGSEGV; } - - return 0; + displ <<= 16; + displ |= word2; + insn->is_advance += 2; + } else /* displacement is word sized */ + if (displ & 0x8000) + displ |= 0xFFFF0000; + + /* XXX: If CC, frame->f_pc += displ */ + 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; /* caught an exception */ + } + if (sig == -1) { + /* branch does take place; 2 is the offset to the 1st disp word */ + insn->is_advance = displ + 2; + } else if (sig) { + return SIGILL; /* got a signal */ + } + if (fpu_debug_level & DL_BRANCH) { + printf(" fpu_emul_brcc: %s insn @ %x (%x+%x) (disp=%x)\n", + (sig == -1) ? "BRANCH to" : "NEXT", + frame->f_pc + insn->is_advance, frame->f_pc, insn->is_advance, + displ); + } + return 0; } |