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