diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /sys/arch/m68k/fpe |
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/m68k/fpe')
-rw-r--r-- | sys/arch/m68k/fpe/fpu_emulate.c | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/sys/arch/m68k/fpe/fpu_emulate.c b/sys/arch/m68k/fpe/fpu_emulate.c new file mode 100644 index 00000000000..7fd5cd3dfed --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_emulate.c @@ -0,0 +1,648 @@ +/* $NetBSD: fpu_emulate.c,v 1.2 1995/03/10 01:43:05 gwr Exp $ */ + +/* + * Copyright (c) 1995 Gordon W. Ross + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gordon Ross + * + * 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. + */ + +/* + * mc68881 emulator + * XXX - Just a start at it for now... + */ + +#include <sys/types.h> +#include <sys/signal.h> +#include <machine/frame.h> + +#define DEBUG 1 /* XXX */ + +/* + * 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); + + +/* + * 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) +{ + struct instruction insn; + int word, optype, sig; + + word = fusword(frame->f_pc); + if (word < 0) { +#ifdef DEBUG + printf("fpu_emulate: fault reading opcode\n"); +#endif + return SIGSEGV; + } + + if ((word & 0xF000) != 0xF000) { +#ifdef DEBUG + printf("fpu_emulate: not coproc. insn.: opcode=0x%x\n", word); +#endif + return SIGILL; + } + + if ((word & 0x0E00) != 0x0200) { +#ifdef DEBUG + printf("fpu_emulate: bad coproc. id: opcode=0x%x\n", word); +#endif + return SIGILL; + } + + insn.opcode = word; + optype = (word & 0x01C0); + + word = fusword(frame->f_pc + 2); + if (word < 0) { +#ifdef DEBUG + printf("fpu_emulate: fault reading word1\n"); +#endif + return SIGSEGV; + } + insn.word1 = word; + + /* + * 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); +#endif + sig = SIGILL; + } + + if (sig == 0) { + frame->f_pc += insn.advance; + } +#if defined(DDB) && defined(DEBUG) + else kdb_trap(-1, frame); +#endif + + return (sig); +} + +/* + * type 0: fmovem, fmove <cr> + * Separated out of fpu_emul_type0 for efficiency. + * In this function, we know: + * (opcode & 0x01C0) == 0 + * (word1 & 0x8000) == 0x8000 + * + * 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) +{ + 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; + } + + /* 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]; + + if (insn->ea0.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 = 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; + } + + 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); +} + +/* + * type 1: fdbcc, fscc, ftrapcc + * In this function, we know: + * (opcode & 0x01C0) == 0x0040 + */ +int fpu_emul_type1(struct frame *frame, + struct fpframe *fpf, + struct instruction *insn) +{ + int sig; + + /* Get effective address */ + /* XXX */ + + switch (insn->opcode & 070) { + + case 010: /* fdbcc */ + /* XXX: If not CC { Decrement Dn; if (Dn >= 0) branch; } */ + + case 070: /* fscc or ftrapcc */ + if ((insn->opcode & 07) > 1) { + /* ftrapcc */ + /* XXX: If CC, advance and return SIGFPE */ + break; + } + /* fallthrough */ + default: /* fscc */ + /* XXX: If CC, store ones, else store zero */ + sig = SIGILL; + break; + + } + 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"); +#endif + return SIGSEGV; + } + displ << 16; + displ |= word2; + advance += 2; + } + + /* XXX: If CC, frame->f_pc += displ */ + return SIGILL; +} + +/* + * Helper routines for dealing with "effective address" values. + */ + +/* + * Decode an effective address into internal form. + * Returns zero on success, else signal number. + */ +static int decode_ea(struct frame *frame, + struct instruction *insn, + struct insn_ea *ea, + int modreg) +{ + 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; + + 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 (immed_bytes > 2) { + data = fusword(frame->f_pc + insn->advance); + if (data < 0) + return SIGSEGV; + insn->advance += 2; + ea->immed <<= 16; + ea->immed |= data; + } + return 0; +} + + +/* + * Load a value from an effective address. + * Returns zero on success, else signal number. + */ +static int load_ea(struct frame *frame, + struct instruction *insn, + struct insn_ea *ea, + char *dst) +{ + int *reg; + char *src; + int len; + +#ifdef DIAGNOSTIC + if (ea->regnum & ~0xF) + panic("load_ea: bad regnum"); +#endif + + /* 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); + } 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; + } + + return 0; +} + +/* + * Store a value at the effective address. + * Returns zero on success, else signal number. + */ +static int store_ea(struct frame *frame, + struct instruction *insn, + struct insn_ea *ea, + char *src) +{ + int *reg; + char *dst; + int len; + +#ifdef DIAGNOSTIC + if (ea->regnum & ~0xF) + panic("load_ea: bad regnum"); +#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 0; +} |