Home | History | Annotate | Line # | Download | only in fpe
fpu_emulate.c revision 1.1.1.1
      1 /*	$NetBSD: fpu_emulate.c,v 1.1.1.1 1995/03/01 04:56:27 gwr Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995 Gordon W. Ross
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of the author may not be used to endorse or promote products
     16  *    derived from this software without specific prior written permission.
     17  * 4. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *      This product includes software developed by Gordon Ross
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * mc68881 emulator
     35  * XXX - Just a start at it for now...
     36  */
     37 
     38 #include <sys/types.h>
     39 #include <sys/signal.h>
     40 #include <machine/frame.h>
     41 
     42 #define	DEBUG 1	/* XXX */
     43 
     44 /*
     45  * Internal info about a decoded effective address.
     46  */
     47 struct insn_ea {
     48 	int regnum;
     49 	int immed;
     50 	int flags;
     51 #define	EA_DIRECT	0x01
     52 #define EA_PREDECR	0x02
     53 #define	EA_POSTINCR	0x04
     54 #define EA_OFFSET	0x08	/* mode 5: base+offset */
     55 #define	EA_INDEXED	0x10	/* mode 6: complicated */
     56 #define EA_ABS  	0x20	/* mode 7: reg 0 or 1 */
     57 #define EA_PC_REL	0x40	/* mode 7: reg 2 or 3 */
     58 #define	EA_IMMED	0x80	/* mode 7: reg 4 */
     59 };
     60 
     61 struct instruction {
     62 	int advance;	/* length of instruction */
     63 	int datasize;	/* byte, word, long, float, double, ... */
     64 	int	opcode;
     65 	int word1;
     66 	struct insn_ea ea0;
     67 	struct insn_ea ea1;
     68 };
     69 
     70 int fpu_emul_fmovm(struct frame *frame,
     71 				   struct fpframe *fpf,
     72 				   struct instruction *insn);
     73 int fpu_emul_type0(struct frame *frame,
     74 				   struct fpframe *fpf,
     75 				   struct instruction *insn);
     76 int fpu_emul_type1(struct frame *frame,
     77 				   struct fpframe *fpf,
     78 				   struct instruction *insn);
     79 int fpu_emul_brcc(struct frame *frame,
     80 				  struct fpframe *fpf,
     81 				  struct instruction *insn);
     82 
     83 static int decode_ea(struct frame *frame,
     84 					 struct instruction *insn,
     85 					 struct insn_ea *ea,
     86 					 int modreg);
     87 static int load_ea(struct frame *frame,
     88 				   struct instruction *insn,
     89 				   struct insn_ea *ea,
     90 				   char *cpureg);
     91 static int store_ea(struct frame *frame,
     92 					struct instruction *insn,
     93 					struct insn_ea *ea,
     94 					char *cpureg);
     95 
     96 
     97 /*
     98  * Emulate a floating-point instruction.
     99  * Return zero for success, else signal number.
    100  * (Typically: zero, SIGFPE, SIGILL, SIGSEGV)
    101  */
    102 int fpu_emulate(struct frame *frame, struct fpframe *fpf)
    103 {
    104 	struct instruction insn;
    105 	int word, optype, sig;
    106 
    107 	word = fusword(frame->f_pc);
    108 	if (word < 0) {
    109 #ifdef	DEBUG
    110 		printf("fpu_emulate: fault reading opcode\n");
    111 #endif
    112 		return SIGSEGV;
    113 	}
    114 
    115 	if ((word & 0xF000) != 0xF000) {
    116 #ifdef	DEBUG
    117 		printf("fpu_emulate: not coproc. insn.: opcode=0x%x\n", word);
    118 #endif
    119 		return SIGILL;
    120 	}
    121 
    122 	if ((word & 0x0E00) != 0x0200) {
    123 #ifdef	DEBUG
    124 		printf("fpu_emulate: bad coproc. id: opcode=0x%x\n", word);
    125 #endif
    126 		return SIGILL;
    127 	}
    128 
    129 	insn.opcode = word;
    130 	optype = (word & 0x01C0);
    131 
    132 	word = fusword(frame->f_pc + 2);
    133 	if (word < 0) {
    134 #ifdef	DEBUG
    135 		printf("fpu_emulate: fault reading word1\n");
    136 #endif
    137 		return SIGSEGV;
    138 	}
    139 	insn.word1 = word;
    140 
    141 	/*
    142 	 * Which family (or type) of opcode is it?
    143 	 * Tests ordered by likelihood (hopefully).
    144 	 * Certainly, type 0 is the most common.
    145 	 */
    146 	if (optype == 0x0000) {
    147 		/* type=0: generic */
    148 		if (insn.word1 & 0x8000) {
    149 			sig = fpu_emul_fmovm(frame, fpf, &insn);
    150 		} else {
    151 			sig = fpu_emul_type0(frame, fpf, &insn);
    152 		}
    153 	}
    154 	else if (optype == 0x0080) {
    155 		/* type=2: fbcc, short disp. */
    156 		sig = fpu_emul_brcc(frame, fpf, &insn);
    157 	}
    158 	else if (optype == 0x00C0) {
    159 		/* type=3: fbcc, long disp. */
    160 		sig = fpu_emul_brcc(frame, fpf, &insn);
    161 	}
    162 	else if (optype == 0x0040) {
    163 		/* type=1: fdbcc, fscc, ftrapcc */
    164 		sig = fpu_emul_type1(frame, fpf, &insn);
    165 	}
    166 	else {
    167 		/* type=4: fsave    (privileged) */
    168 		/* type=5: frestore (privileged) */
    169 		/* type=6: reserved */
    170 		/* type=7: reserved */
    171 #ifdef	DEBUG
    172 		printf("fpu_emulate: bad opcode type: opcode=0x%x\n", insn.opcode);
    173 #endif
    174 		sig = SIGILL;
    175 	}
    176 
    177 	if (sig == 0) {
    178 		frame->f_pc += insn.advance;
    179 	}
    180 #if defined(DDB) && defined(DEBUG)
    181 	else kdb_trap(-1, frame);
    182 #endif
    183 
    184 	return (sig);
    185 }
    186 
    187 /*
    188  * type 0: fmovem, fmove <cr>
    189  * Separated out of fpu_emul_type0 for efficiency.
    190  * In this function, we know:
    191  *   (opcode & 0x01C0) == 0
    192  *   (word1 & 0x8000) == 0x8000
    193  *
    194  * No conversion or rounding is done by this instruction,
    195  * and the FPSR is not affected.
    196  */
    197 int fpu_emul_fmovm(struct frame *frame,
    198 				   struct fpframe *fpf,
    199 				   struct instruction *insn)
    200 {
    201 	int word1, sig;
    202 	int reglist, regmask, regnum;
    203 	int fpu_to_mem, order;
    204 	int w1_post_incr;	/* XXX - FP regs order? */
    205 	int *fpregs;
    206 
    207 	insn->advance = 4;
    208 	insn->datasize = 12;
    209 	word1 = insn->word1;
    210 
    211 	/* Bit 14 selects FPn or FP control regs. */
    212 	if (word1 & 0x4000) {
    213 		/*
    214 		 * Bits 12,11 select register list mode:
    215 		 * 0,0: Static  reg list, pre-decr.
    216 		 * 0,1: Dynamic reg list, pre-decr.
    217 		 * 1,0: Static  reg list, post-incr.
    218 		 * 1,1: Dynamic reg list, post-incr
    219 		 */
    220 		w1_post_incr = word1 & 0x1000;
    221 		if (word1 & 0x0800) {
    222 			/* dynamic reg list */
    223 			reglist = frame->f_regs[(word1 & 0x70) >> 4];
    224 		} else
    225 			reglist = word1;
    226 		reglist &= 0xFF;
    227 	} else {
    228 		/* XXX: move to/from control registers */
    229 		reglist = word1 & 0x1C00;
    230 		return SIGILL;
    231 	}
    232 
    233 	/* Bit 13 selects direction (FPU to/from Mem) */
    234 	fpu_to_mem = word1 & 0x2000;
    235 
    236 	/* Get effective address. (modreg=opcode&077) */
    237 	sig = decode_ea(frame, insn, &insn->ea0, insn->opcode);
    238 	if (sig) return sig;
    239 
    240 	/* Get address of soft coprocessor regs. */
    241 	fpregs = &fpf->fpf_regs[0];
    242 
    243 	if (insn->ea0.flags & EA_PREDECR) {
    244 		regnum = 7;
    245 		order = -1;
    246 	} else {
    247 		regnum = 0;
    248 		order = 1;
    249 	}
    250 
    251 	while ((0 <= regnum) && (regnum < 8)) {
    252 		regmask = 1 << regnum;
    253 		if (regmask & reglist) {
    254 			if (fpu_to_mem)
    255 				sig = store_ea(frame, insn, &insn->ea0,
    256 							   (char*) &fpregs[regnum]);
    257 			else /* mem to fpu */
    258 				sig = load_ea(frame, insn, &insn->ea0,
    259 							  (char*) &fpregs[regnum]);
    260 			if (sig) break;
    261 		}
    262 		regnum += order;
    263 	}
    264 
    265 	return 0;
    266 }
    267 
    268 int fpu_emul_type0(struct frame *frame,
    269 				   struct fpframe *fpf,
    270 				   struct instruction *insn)
    271 {
    272 	int sig;
    273 
    274 	/* Get effective address */
    275 	/* XXX */
    276 
    277 	switch(insn->word1 & 0x3F) {
    278 
    279 	case 0x00:	/* fmove */
    280 
    281 	case 0x01:	/* fint */
    282 	case 0x02:	/* fsinh */
    283 	case 0x03:	/* fintrz */
    284 	case 0x04:	/* fsqrt */
    285 	case 0x06:	/* flognp1 */
    286 
    287 	case 0x09:	/* ftanh */
    288 	case 0x0A:	/* fatan */
    289 	case 0x0C:	/* fasin */
    290 	case 0x0D:	/* fatanh */
    291 	case 0x0E:	/* fsin */
    292 	case 0x0F:	/* ftan */
    293 
    294 	case 0x10:	/* fetox */
    295 	case 0x11:	/* ftwotox */
    296 	case 0x12:	/* ftentox */
    297 	case 0x14:	/* flogn */
    298 	case 0x15:	/* flog10 */
    299 	case 0x16:	/* flog2 */
    300 
    301 	case 0x18:	/* fabs */
    302 	case 0x19:	/* fcosh */
    303 	case 0x1A:	/* fneg */
    304 	case 0x1C:	/* facos */
    305 	case 0x1D:	/* fcos */
    306 	case 0x1E:	/* fgetexp */
    307 	case 0x1F:	/* fgetman */
    308 
    309 	case 0x20:	/* fdiv */
    310 	case 0x21:	/* fmod */
    311 	case 0x22:	/* fadd */
    312 	case 0x23:	/* fmul */
    313 	case 0x24:	/* fsgldiv */
    314 	case 0x25:	/* frem */
    315 	case 0x26:	/* fscale */
    316 	case 0x27:	/* fsglmul */
    317 
    318 	case 0x28:	/* fsub */
    319 	case 0x38:	/* fcmp */
    320 	case 0x3A:	/* ftst */
    321 
    322 	default:
    323 #ifdef	DEBUG
    324 		printf("fpu_emul_type0: unknown: opcode=0x%x, word1=0x%x\n",
    325 			   insn->opcode, insn->word1);
    326 #endif
    327 		sig = SIGILL;
    328 
    329 	} /* switch */
    330 	return (sig);
    331 }
    332 
    333 /*
    334  * type 1: fdbcc, fscc, ftrapcc
    335  * In this function, we know:
    336  *   (opcode & 0x01C0) == 0x0040
    337  */
    338 int fpu_emul_type1(struct frame *frame,
    339 				   struct fpframe *fpf,
    340 				   struct instruction *insn)
    341 {
    342 	int sig;
    343 
    344 	/* Get effective address */
    345 	/* XXX */
    346 
    347 	switch (insn->opcode & 070) {
    348 
    349 	case 010:	/* fdbcc */
    350 		/* XXX: If not CC { Decrement Dn; if (Dn >= 0) branch; } */
    351 
    352 	case 070:	/* fscc or ftrapcc */
    353 		if ((insn->opcode & 07) > 1) {
    354 			/* ftrapcc */
    355 			/* XXX: If CC, advance and return SIGFPE */
    356 			break;
    357 		}
    358 		/* fallthrough */
    359 	default:	/* fscc */
    360 		/* XXX: If CC, store ones, else store zero */
    361 		sig = SIGILL;
    362 		break;
    363 
    364 	}
    365 	return (sig);
    366 }
    367 
    368 /*
    369  * Type 2 or 3: fbcc (also fnop)
    370  * In this function, we know:
    371  *   (opcode & 0x0180) == 0x0080
    372  */
    373 int fpu_emul_brcc(struct frame *frame,
    374 				  struct fpframe *fpf,
    375 				  struct instruction *insn)
    376 {
    377 	int displ, word2;
    378 	int sig, advance;
    379 
    380 	/*
    381 	 * Get branch displacement.
    382 	 */
    383 	advance = 4;
    384 	displ = insn->word1;
    385 	if (displ & 0x8000)
    386 		displ |= 0xFFFF0000;
    387 
    388 	if (insn->opcode & 0x40) {
    389 		word2 = fusword(frame->f_pc + 4);
    390 		if (word2 < 0) {
    391 #ifdef	DEBUG
    392 			printf("fpu_emul_brcc: fault reading word2\n");
    393 #endif
    394 			return SIGSEGV;
    395 		}
    396 		displ << 16;
    397 		displ |= word2;
    398 		advance += 2;
    399 	}
    400 
    401 	/* XXX: If CC, frame->f_pc += displ */
    402 	return SIGILL;
    403 }
    404 
    405 /*
    406  * Helper routines for dealing with "effective address" values.
    407  */
    408 
    409 /*
    410  * Decode an effective address into internal form.
    411  * Returns zero on success, else signal number.
    412  */
    413 static int decode_ea(struct frame *frame,
    414 					 struct instruction *insn,
    415 					 struct insn_ea *ea,
    416 					 int modreg)
    417 {
    418 	int immed_bytes = 0;
    419 	int data;
    420 
    421 	/* Set the most common value here. */
    422 	ea->regnum = 8 + (modreg & 7);
    423 
    424 	switch (modreg & 070) {
    425 
    426 	case 0:	/* Dn */
    427 		ea->regnum = (modreg & 7);
    428 		ea->flags = EA_DIRECT;
    429 		break;
    430 
    431 	case 010:	/* An */
    432 		ea->flags = EA_DIRECT;
    433 		break;
    434 
    435 	case 020:	/* (An) */
    436 		ea->flags = 0;
    437 		break;
    438 
    439 	case 030: /* (An)+ */
    440 		ea->flags = EA_POSTINCR;
    441 		break;
    442 
    443 	case 040: /* -(An) */
    444 		ea->flags = EA_PREDECR;
    445 		break;
    446 
    447 	case 050: /* (d16,An) */
    448 		ea->flags = EA_OFFSET;
    449 		immed_bytes = 2;
    450 		break;
    451 
    452 	case 060:	/* (d8,An,Xn) */
    453 		ea->flags = EA_INDEXED;
    454 		immed_bytes = 2;
    455 
    456 	case 070:	/* misc. */
    457 		ea->regnum = (modreg & 7);
    458 		switch (modreg & 7) {
    459 
    460 		case 0: /* (xxxx).W */
    461 			ea->flags = EA_ABS;
    462 			immed_bytes = 2;
    463 			break;
    464 
    465 		case 1: /* (xxxxxxxx).L */
    466 			ea->flags = EA_ABS;
    467 			immed_bytes = 4;
    468 			break;
    469 
    470 		case 2: /* (d16,PC) */
    471 			ea->flags = EA_PC_REL | EA_OFFSET;
    472 			immed_bytes = 2;
    473 			break;
    474 
    475 		case 3: /* (d8,PC,Xn) */
    476 			ea->flags = EA_PC_REL | EA_INDEXED;
    477 			immed_bytes = 2;
    478 			break;
    479 
    480 		case 4: /* #data */
    481 			ea->flags = EA_IMMED;
    482 			immed_bytes = insn->datasize;
    483 			break;
    484 
    485 		default:
    486 			return SIGILL;
    487 		} /* switch for mode 7 */
    488 		break;
    489 	} /* switch mode */
    490 
    491 	/* Now fetch any immediate data and advance. */
    492 	if (immed_bytes > 0) {
    493 		data = fusword(frame->f_pc + insn->advance);
    494 		if (data < 0)
    495 			return SIGSEGV;
    496 		insn->advance += 2;
    497 		if (data & 0x8000)
    498 			data |= 0xFFFF0000;
    499 		ea->immed = data;
    500 	}
    501 	if (immed_bytes > 2) {
    502 		data = fusword(frame->f_pc + insn->advance);
    503 		if (data < 0)
    504 			return SIGSEGV;
    505 		insn->advance += 2;
    506 		ea->immed <<= 16;
    507 		ea->immed |= data;
    508 	}
    509 	return 0;
    510 }
    511 
    512 
    513 /*
    514  * Load a value from an effective address.
    515  * Returns zero on success, else signal number.
    516  */
    517 static int load_ea(struct frame *frame,
    518 				   struct instruction *insn,
    519 				   struct insn_ea *ea,
    520 				   char *dst)
    521 {
    522 	int *reg;
    523 	char *src;
    524 	int len;
    525 
    526 #ifdef	DIAGNOSTIC
    527 	if (ea->regnum & ~0xF)
    528 		panic("load_ea: bad regnum");
    529 #endif
    530 
    531 	/* The dst is always int or larger. */
    532 	len = insn->datasize;
    533 	if (len < 4)
    534 		dst += (4 - len);
    535 
    536 	/* point to the register */
    537 	if (ea->flags & EA_PC_REL)
    538 		reg = &frame->f_pc;
    539 	else
    540 		reg = &frame->f_regs[ea->regnum];
    541 
    542 	if (ea->flags & (EA_DIRECT | EA_IMMED)) {
    543 		if (ea->flags & EA_DIRECT)
    544 			src = (char*) reg;
    545 		if (ea->flags & EA_IMMED)
    546 			src = (char*) &ea->immed;
    547 		if (len > 4)
    548 			return SIGILL;
    549 		/* The source is an int. */
    550 		if (len < 4)
    551 			src += (4 - len);
    552 		bcopy(src, dst, len);
    553 	} else {
    554 		/* One of MANY indirect forms... */
    555 
    556 		/* do pre-decrement */
    557 		if (ea->flags & EA_PREDECR)
    558 			*reg -= len;
    559 
    560 		/* Grab the register contents. */
    561 		src = (char*) *reg;
    562 
    563 		/* apply the signed offset */
    564 		if (ea->flags & EA_OFFSET)
    565 			src += ea->immed;
    566 
    567 		/* XXX - Don't know how to handle this yet. */
    568 		if (ea->flags & EA_INDEXED)
    569 			return SIGILL;
    570 
    571 		copyin(src, dst, len);
    572 
    573 		/* do post-increment */
    574 		if (ea->flags & EA_POSTINCR)
    575 			*reg += len;
    576 	}
    577 
    578 	return 0;
    579 }
    580 
    581 /*
    582  * Store a value at the effective address.
    583  * Returns zero on success, else signal number.
    584  */
    585 static int store_ea(struct frame *frame,
    586 					struct instruction *insn,
    587 					struct insn_ea *ea,
    588 					char *src)
    589 {
    590 	int *reg;
    591 	char *dst;
    592 	int len;
    593 
    594 #ifdef	DIAGNOSTIC
    595 	if (ea->regnum & ~0xF)
    596 		panic("load_ea: bad regnum");
    597 #endif
    598 
    599 	/* The src is always int or larger. */
    600 	len = insn->datasize;
    601 	if (len < 4)
    602 		src += (4 - len);
    603 
    604 	/* point to the register */
    605 	if (ea->flags & EA_PC_REL)
    606 		reg = &frame->f_pc;
    607 	else
    608 		reg = &frame->f_regs[ea->regnum];
    609 
    610 	if (ea->flags & EA_IMMED)
    611 		return SIGILL;
    612 
    613 	if (ea->flags & EA_DIRECT) {
    614 		dst = (char*) reg;
    615 		if (len > 4)
    616 			return SIGILL;
    617 		/* The destination is an int. */
    618 		if (len < 4)
    619 			dst += (4 - len);
    620 		bcopy(src, dst, len);
    621 	} else {
    622 		/* One of MANY indirect forms... */
    623 
    624 		/* do pre-decrement */
    625 		if (ea->flags & EA_PREDECR)
    626 			*reg -= len;
    627 
    628 		/* Grab the register contents. */
    629 		dst = (char*) *reg;
    630 
    631 		/* apply the signed offset */
    632 		if (ea->flags & EA_OFFSET)
    633 			dst += ea->immed;
    634 
    635 		/* XXX - Don't know how to handle this yet. */
    636 		if (ea->flags & EA_INDEXED)
    637 			return SIGILL;
    638 
    639 		copyout(src, dst, len);
    640 
    641 		/* do post-increment */
    642 		if (ea->flags & EA_POSTINCR)
    643 			*reg += len;
    644 	}
    645 
    646 	return 0;
    647 }
    648