fpu_calcea.c revision 1.3 1 /* $NetBSD: fpu_calcea.c,v 1.3 1996/02/04 02:17:38 briggs Exp $ */
2
3 /*
4 * Copyright (c) 1995 Gordon W. Ross
5 * portion Copyright (c) 1995 Ken Nakata
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 * 4. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Gordon Ross
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/param.h>
35 #include <sys/signal.h>
36 #include <machine/frame.h>
37
38 #include "fpu_emulate.h"
39
40 /*
41 * Prototypes of static functions
42 */
43 static int decode_ea6 __P((struct frame *frame, struct instruction *insn,
44 struct insn_ea *ea, int modreg));
45 static int fetch_immed __P((struct frame *frame, struct instruction *insn,
46 int *dst));
47 static int fetch_disp __P((struct frame *frame, struct instruction *insn,
48 int size, int *res));
49 static int calc_ea __P((struct insn_ea *ea, char *ptr, char **eaddr));
50
51 /*
52 * Helper routines for dealing with "effective address" values.
53 */
54
55 /*
56 * Decode an effective address into internal form.
57 * Returns zero on success, else signal number.
58 */
59 int
60 fpu_decode_ea(frame, insn, ea, modreg)
61 struct frame *frame;
62 struct instruction *insn;
63 struct insn_ea *ea;
64 int modreg;
65 {
66 int data, sig;
67
68 #ifdef DEBUG
69 if (insn->is_datasize < 0) {
70 panic("decode_ea: called with uninitialized datasize\n");
71 }
72 #endif
73
74 sig = 0;
75
76 /* Set the most common value here. */
77 ea->ea_regnum = 8 + (modreg & 7);
78
79 switch (modreg & 070) {
80 case 0: /* Dn */
81 ea->ea_regnum &= 7;
82 case 010: /* An */
83 ea->ea_flags = EA_DIRECT;
84 if (fpu_debug_level & DL_DECODEEA) {
85 printf(" decode_ea: register direct reg=%d\n", ea->ea_regnum);
86 }
87 break;
88
89 case 020: /* (An) */
90 ea->ea_flags = 0;
91 if (fpu_debug_level & DL_DECODEEA) {
92 printf(" decode_ea: register indirect reg=%d\n", ea->ea_regnum);
93 }
94 break;
95
96 case 030: /* (An)+ */
97 ea->ea_flags = EA_POSTINCR;
98 if (fpu_debug_level & DL_DECODEEA) {
99 printf(" decode_ea: reg indirect postincrement reg=%d\n",
100 ea->ea_regnum);
101 }
102 break;
103
104 case 040: /* -(An) */
105 ea->ea_flags = EA_PREDECR;
106 if (fpu_debug_level & DL_DECODEEA) {
107 printf(" decode_ea: reg indirect predecrement reg=%d\n",
108 ea->ea_regnum);
109 }
110 break;
111
112 case 050: /* (d16,An) */
113 ea->ea_flags = EA_OFFSET;
114 sig = fetch_disp(frame, insn, 1, &ea->ea_offset);
115 if (fpu_debug_level & DL_DECODEEA) {
116 printf(" decode_ea: reg indirect with displacement reg=%d\n",
117 ea->ea_regnum);
118 }
119 break;
120
121 case 060: /* (d8,An,Xn) */
122 ea->ea_flags = EA_INDEXED;
123 sig = decode_ea6(frame, insn, ea, modreg);
124 break;
125
126 case 070: /* misc. */
127 ea->ea_regnum = (modreg & 7);
128 switch (modreg & 7) {
129
130 case 0: /* (xxxx).W */
131 ea->ea_flags = EA_ABS;
132 sig = fetch_disp(frame, insn, 1, &ea->ea_absaddr);
133 if (fpu_debug_level & DL_DECODEEA) {
134 printf(" decode_ea: absolute address (word)\n");
135 }
136 break;
137
138 case 1: /* (xxxxxxxx).L */
139 ea->ea_flags = EA_ABS;
140 sig = fetch_disp(frame, insn, 2, &ea->ea_absaddr);
141 if (fpu_debug_level & DL_DECODEEA) {
142 printf(" decode_ea: absolute address (long)\n");
143 }
144 break;
145
146 case 2: /* (d16,PC) */
147 ea->ea_flags = EA_PC_REL | EA_OFFSET;
148 sig = fetch_disp(frame, insn, 1, &ea->ea_absaddr);
149 if (fpu_debug_level & DL_DECODEEA) {
150 printf(" decode_ea: pc relative word displacement\n");
151 }
152 break;
153
154 case 3: /* (d8,PC,Xn) */
155 ea->ea_flags = EA_PC_REL | EA_INDEXED;
156 sig = decode_ea6(frame, insn, ea, modreg);
157 break;
158
159 case 4: /* #data */
160 ea->ea_flags = EA_IMMED;
161 sig = fetch_immed(frame, insn, &ea->ea_immed[0]);
162 if (fpu_debug_level & DL_DECODEEA) {
163 printf(" decode_ea: immediate size=%d\n", insn->is_datasize);
164 }
165 break;
166
167 default:
168 if (fpu_debug_level & DL_DECODEEA) {
169 printf(" decode_ea: invalid addr mode (7,%d)\n", modreg & 7);
170 }
171 return SIGILL;
172 } /* switch for mode 7 */
173 break;
174 } /* switch mode */
175
176 ea->ea_tdisp = 0;
177
178 return sig;
179 }
180
181 /*
182 * Decode Mode=6 address modes
183 */
184 static int
185 decode_ea6(frame, insn, ea, modreg)
186 struct frame *frame;
187 struct instruction *insn;
188 struct insn_ea *ea;
189 int modreg;
190 {
191 int word, extword, idx;
192 int basedisp, outerdisp;
193 int bd_size, od_size;
194 int sig;
195
196 extword = fusword(frame->f_pc + insn->is_advance);
197 if (extword < 0) {
198 return SIGSEGV;
199 }
200 insn->is_advance += 2;
201
202 /* get register index */
203 ea->ea_idxreg = (extword >> 12) & 0xf;
204 idx = frame->f_regs[ea->ea_idxreg];
205 if ((extword & 0x0800) == 0) {
206 /* if word sized index, sign-extend */
207 idx &= 0xffff;
208 if (idx & 0x8000) {
209 idx |= 0xffff0000;
210 }
211 }
212 /* scale register index */
213 idx <<= ((extword >>9) & 3);
214
215 if ((extword & 0x100) == 0) {
216 /* brief extention word - sign-extend the displacement */
217 basedisp = (extword & 0xff);
218 if (basedisp & 0x80) {
219 basedisp |= 0xffffff00;
220 }
221
222 ea->ea_basedisp = idx + basedisp;
223 ea->ea_outerdisp = 0;
224 if (fpu_debug_level & DL_DECODEEA) {
225 printf(" decode_ea6: brief ext word idxreg=%d, basedisp=%08x\n",
226 ea->ea_idxreg, ea->ea_basedisp);
227 }
228 } else {
229 /* full extention word */
230 if (extword & 0x80) {
231 ea->ea_flags |= EA_BASE_SUPPRSS;
232 }
233 bd_size = ((extword >> 4) & 3) - 1;
234 od_size = (extword & 3) - 1;
235 sig = fetch_disp(frame, insn, bd_size, &basedisp);
236 if (sig) {
237 return sig;
238 }
239 if (od_size >= 0) {
240 ea->ea_flags |= EA_MEM_INDIR;
241 }
242 sig = fetch_disp(frame, insn, od_size, &outerdisp);
243 if (sig) {
244 return sig;
245 }
246
247 switch (extword & 0x44) {
248 case 0: /* preindexed */
249 ea->ea_basedisp = basedisp + idx;
250 ea->ea_outerdisp = outerdisp;
251 break;
252 case 4: /* postindexed */
253 ea->ea_basedisp = basedisp;
254 ea->ea_outerdisp = outerdisp + idx;
255 break;
256 case 0x40: /* no index */
257 ea->ea_basedisp = basedisp;
258 ea->ea_outerdisp = outerdisp;
259 break;
260 default:
261 #ifdef DEBUG
262 printf(" decode_ea6: invalid indirect mode: ext word %04x\n",
263 extword);
264 #endif
265 return SIGILL;
266 break;
267 }
268 if (fpu_debug_level & DL_DECODEEA) {
269 printf(" decode_ea6: full ext idxreg=%d, basedisp=%x, outerdisp=%x\n",
270 ea->ea_idxreg, ea->ea_basedisp, ea->ea_outerdisp);
271 }
272 }
273 if (fpu_debug_level & DL_DECODEEA) {
274 printf(" decode_ea6: regnum=%d, flags=%x\n",
275 ea->ea_regnum, ea->ea_flags);
276 }
277 return 0;
278 }
279
280 /*
281 * Load a value from an effective address.
282 * Returns zero on success, else signal number.
283 */
284 int
285 fpu_load_ea(frame, insn, ea, dst)
286 struct frame *frame;
287 struct instruction *insn;
288 struct insn_ea *ea;
289 char *dst;
290 {
291 int *reg;
292 char *src;
293 int len, step;
294 int data, word, sig;
295
296 #ifdef DIAGNOSTIC
297 if (ea->ea_regnum & ~0xF) {
298 panic(" load_ea: bad regnum");
299 }
300 #endif
301
302 if (fpu_debug_level & DL_LOADEA) {
303 printf(" load_ea: frame at %08x\n", frame);
304 }
305 /* The dst is always int or larger. */
306 len = insn->is_datasize;
307 if (len < 4) {
308 dst += (4 - len);
309 }
310 step = (len == 1 && ea->ea_regnum == 15 /* sp */) ? 2 : len;
311
312 if (ea->ea_flags & EA_DIRECT) {
313 if (len > 4) {
314 #ifdef DEBUG
315 printf(" load_ea: operand doesn't fit cpu reg\n");
316 #endif
317 return SIGILL;
318 }
319 if (ea->ea_tdisp > 0) {
320 #ifdef DEBUG
321 printf(" load_ea: more than one move from cpu reg\n");
322 #endif
323 return SIGILL;
324 }
325 src = (char *)&frame->f_regs[ea->ea_regnum];
326 /* The source is an int. */
327 if (len < 4) {
328 src += (4 - len);
329 if (fpu_debug_level & DL_LOADEA) {
330 printf(" load_ea: short/byte opr - addr adjusted\n");
331 }
332 }
333 if (fpu_debug_level & DL_LOADEA) {
334 printf(" load_ea: src 0x%08x\n", src);
335 }
336 bcopy(src, dst, len);
337 } else if (ea->ea_flags & EA_IMMED) {
338 if (fpu_debug_level & DL_LOADEA) {
339 printf(" load_ea: immed %08x%08x%08x size %d\n",
340 ea->ea_immed[0], ea->ea_immed[1], ea->ea_immed[2], len);
341 }
342 src = (char *)&ea->ea_immed[0];
343 if (len < 4) {
344 src += (4 - len);
345 if (fpu_debug_level & DL_LOADEA) {
346 printf(" load_ea: short/byte immed opr - addr adjusted\n");
347 }
348 }
349 bcopy(src, dst, len);
350 } else if (ea->ea_flags & EA_ABS) {
351 if (fpu_debug_level & DL_LOADEA) {
352 printf(" load_ea: abs addr %08x\n", ea->ea_absaddr);
353 }
354 src = (char *)ea->ea_absaddr;
355 copyin(src, dst, len);
356 } else /* register indirect */ {
357 if (ea->ea_flags & EA_PC_REL) {
358 if (fpu_debug_level & DL_LOADEA) {
359 printf(" load_ea: using PC\n");
360 }
361 reg = NULL;
362 /* Grab the register contents. 4 is offset to the first
363 extention word from the opcode */
364 src = (char *)frame->f_pc + 4;
365 if (fpu_debug_level & DL_LOADEA) {
366 printf(" load_ea: pc relative pc+4 = 0x%08x\n", src);
367 }
368 } else /* not PC relative */ {
369 if (fpu_debug_level & DL_LOADEA) {
370 printf(" load_ea: using register %c%d\n",
371 (ea->ea_regnum >= 8) ? 'a' : 'd', ea->ea_regnum & 7);
372 }
373 /* point to the register */
374 reg = &frame->f_regs[ea->ea_regnum];
375
376 if (ea->ea_flags & EA_PREDECR) {
377 if (fpu_debug_level & DL_LOADEA) {
378 printf(" load_ea: predecr mode - reg decremented\n");
379 }
380 *reg -= step;
381 ea->ea_tdisp = 0;
382 }
383
384 /* Grab the register contents. */
385 src = (char *)*reg;
386 if (fpu_debug_level & DL_LOADEA) {
387 printf(" load_ea: reg indirect reg = 0x%08x\n", src);
388 }
389 }
390
391 sig = calc_ea(ea, src, &src);
392 if (sig)
393 return sig;
394
395 copyin(src + ea->ea_tdisp, dst, len);
396
397 /* do post-increment */
398 if (ea->ea_flags & EA_POSTINCR) {
399 if (ea->ea_flags & EA_PC_REL) {
400 #ifdef DEBUG
401 printf(" load_ea: tried to postincrement PC\n");
402 #endif
403 return SIGILL;
404 }
405 *reg += step;
406 ea->ea_tdisp = 0;
407 if (fpu_debug_level & DL_LOADEA) {
408 printf(" load_ea: postinc mode - reg incremented\n");
409 }
410 } else {
411 ea->ea_tdisp += len;
412 }
413 }
414
415 return 0;
416 }
417
418 /*
419 * Store a value at the effective address.
420 * Returns zero on success, else signal number.
421 */
422 int
423 fpu_store_ea(frame, insn, ea, src)
424 struct frame *frame;
425 struct instruction *insn;
426 struct insn_ea *ea;
427 char *src;
428 {
429 int *reg;
430 char *dst;
431 int len, step;
432 int data, word, sig;
433
434 #ifdef DIAGNOSTIC
435 if (ea->ea_regnum & ~0xF) {
436 panic(" store_ea: bad regnum");
437 }
438 #endif
439
440 if (ea->ea_flags & (EA_IMMED|EA_PC_REL)) {
441 /* not alterable address mode */
442 #ifdef DEBUG
443 printf(" store_ea: not alterable address mode\n");
444 #endif
445 return SIGILL;
446 }
447
448 if (fpu_debug_level & DL_STOREEA) {
449 printf(" store_ea: frame at %08x\n", frame);
450 }
451 /* The src is always int or larger. */
452 len = insn->is_datasize;
453 if (len < 4) {
454 src += (4 - len);
455 }
456 step = (len == 1 && ea->ea_regnum == 15 /* sp */) ? 2 : len;
457
458 if (ea->ea_flags & EA_ABS) {
459 if (fpu_debug_level & DL_STOREEA) {
460 printf(" store_ea: abs addr %08x\n", ea->ea_absaddr);
461 }
462 dst = (char *)ea->ea_absaddr;
463 copyout(src, dst + ea->ea_tdisp, len);
464 ea->ea_tdisp += len;
465 } else if (ea->ea_flags & EA_DIRECT) {
466 if (len > 4) {
467 #ifdef DEBUG
468 printf(" store_ea: operand doesn't fit cpu reg\n");
469 #endif
470 return SIGILL;
471 }
472 if (ea->ea_tdisp > 0) {
473 #ifdef DEBUG
474 printf(" store_ea: more than one move to cpu reg\n");
475 #endif
476 return SIGILL;
477 }
478 dst = (char*)&frame->f_regs[ea->ea_regnum];
479 /* The destination is an int. */
480 if (len < 4) {
481 dst += (4 - len);
482 if (fpu_debug_level & DL_STOREEA) {
483 printf(" store_ea: short/byte opr - dst addr adjusted\n");
484 }
485 }
486 if (fpu_debug_level & DL_STOREEA) {
487 printf(" store_ea: dst 0x%08x\n", dst);
488 }
489 bcopy(src, dst, len);
490 } else /* One of MANY indirect forms... */ {
491 if (fpu_debug_level & DL_STOREEA) {
492 printf(" store_ea: using register %c%d\n",
493 (ea->ea_regnum >= 8) ? 'a' : 'd', ea->ea_regnum & 7);
494 }
495 /* point to the register */
496 reg = &(frame->f_regs[ea->ea_regnum]);
497
498 /* do pre-decrement */
499 if (ea->ea_flags & EA_PREDECR) {
500 if (fpu_debug_level & DL_STOREEA) {
501 printf(" store_ea: predecr mode - reg decremented\n");
502 }
503 *reg -= step;
504 ea->ea_tdisp = 0;
505 }
506
507 /* calculate the effective address */
508 sig = calc_ea(ea, (char *)*reg, &dst);
509 if (sig)
510 return sig;
511
512 if (fpu_debug_level & DL_STOREEA) {
513 printf(" store_ea: dst addr=0x%08x+%d\n", dst, ea->ea_tdisp);
514 }
515 copyout(src, dst + ea->ea_tdisp, len);
516
517 /* do post-increment */
518 if (ea->ea_flags & EA_POSTINCR) {
519 *reg += step;
520 ea->ea_tdisp = 0;
521 if (fpu_debug_level & DL_STOREEA) {
522 printf(" store_ea: postinc mode - reg incremented\n");
523 }
524 } else {
525 ea->ea_tdisp += len;
526 }
527 }
528
529 return 0;
530 }
531
532 /*
533 * fetch_immed: fetch immediate operand
534 */
535 static int
536 fetch_immed(frame, insn, dst)
537 struct frame *frame;
538 struct instruction *insn;
539 int *dst;
540 {
541 int data, ext_bytes;
542
543 ext_bytes = insn->is_datasize;
544
545 if (0 < ext_bytes) {
546 data = fusword(frame->f_pc + insn->is_advance);
547 if (data < 0) {
548 return SIGSEGV;
549 }
550 if (ext_bytes == 1) {
551 /* sign-extend byte to long */
552 data &= 0xff;
553 if (data & 0x80) {
554 data |= 0xffffff00;
555 }
556 } else if (ext_bytes == 2) {
557 /* sign-extend word to long */
558 data &= 0xffff;
559 if (data & 0x8000) {
560 data |= 0xffff0000;
561 }
562 }
563 insn->is_advance += 2;
564 dst[0] = data;
565 }
566 if (2 < ext_bytes) {
567 data = fusword(frame->f_pc + insn->is_advance);
568 if (data < 0) {
569 return SIGSEGV;
570 }
571 insn->is_advance += 2;
572 dst[0] <<= 16;
573 dst[0] |= data;
574 }
575 if (4 < ext_bytes) {
576 data = fusword(frame->f_pc + insn->is_advance);
577 if (data < 0) {
578 return SIGSEGV;
579 }
580 dst[1] = data << 16;
581 data = fusword(frame->f_pc + insn->is_advance + 2);
582 if (data < 0) {
583 return SIGSEGV;
584 }
585 insn->is_advance += 4;
586 dst[1] |= data;
587 }
588 if (8 < ext_bytes) {
589 data = fusword(frame->f_pc + insn->is_advance);
590 if (data < 0) {
591 return SIGSEGV;
592 }
593 dst[2] = data << 16;
594 data = fusword(frame->f_pc + insn->is_advance + 2);
595 if (data < 0) {
596 return SIGSEGV;
597 }
598 insn->is_advance += 4;
599 dst[2] |= data;
600 }
601
602 return 0;
603 }
604
605 /*
606 * fetch_disp: fetch displacement in full extention words
607 */
608 static int
609 fetch_disp(frame, insn, size, res)
610 struct frame *frame;
611 struct instruction *insn;
612 int size, *res;
613 {
614 int disp, word;
615
616 if (size == 1) {
617 word = fusword(frame->f_pc + insn->is_advance);
618 if (word < 0) {
619 return SIGSEGV;
620 }
621 disp = word & 0xffff;
622 if (disp & 0x8000) {
623 /* sign-extend */
624 disp |= 0xffff0000;
625 }
626 insn->is_advance += 2;
627 } else if (size == 2) {
628 word = fusword(frame->f_pc + insn->is_advance);
629 if (word < 0) {
630 return SIGSEGV;
631 }
632 disp = word << 16;
633 word = fusword(frame->f_pc + insn->is_advance + 2);
634 if (word < 0) {
635 return SIGSEGV;
636 }
637 disp |= (word & 0xffff);
638 insn->is_advance += 4;
639 } else {
640 disp = 0;
641 }
642 *res = disp;
643 return 0;
644 }
645
646 /*
647 * Calculates an effective address for all address modes except for
648 * register direct, absolute, and immediate modes. However, it does
649 * not take care of predecrement/postincrement of register content.
650 * Returns a signal value (0 == no error).
651 */
652 static int
653 calc_ea(ea, ptr, eaddr)
654 struct insn_ea *ea;
655 char *ptr; /* base address (usually a register content) */
656 char **eaddr; /* pointer to result pointer */
657 {
658 int data, word, sig;
659
660 if (fpu_debug_level & DL_EA) {
661 printf(" calc_ea: reg indirect (reg) = 0x%08x\n", ptr);
662 }
663
664 if (ea->ea_flags & EA_OFFSET) {
665 /* apply the signed offset */
666 if (fpu_debug_level & DL_EA) {
667 printf(" calc_ea: offset %d\n", ea->ea_offset);
668 }
669 ptr += ea->ea_offset;
670 } else if (ea->ea_flags & EA_INDEXED) {
671 if (fpu_debug_level & DL_EA) {
672 printf(" calc_ea: indexed mode\n");
673 }
674
675 if (ea->ea_flags & EA_BASE_SUPPRSS) {
676 /* base register is suppressed */
677 ptr = (char *)ea->ea_basedisp;
678 } else {
679 ptr += ea->ea_basedisp;
680 }
681
682 if (ea->ea_flags & EA_MEM_INDIR) {
683 if (fpu_debug_level & DL_EA) {
684 printf(" calc_ea: mem indir mode: basedisp=%08x, outerdisp=%08x\n",
685 ea->ea_basedisp, ea->ea_outerdisp);
686 printf(" calc_ea: addr fetched from 0x%08x\n", ptr);
687 }
688 /* memory indirect modes */
689 word = fusword(ptr);
690 if (word < 0) {
691 return SIGSEGV;
692 }
693 word <<= 16;
694 data = fusword(ptr + 2);
695 if (data < 0) {
696 return SIGSEGV;
697 }
698 word |= data;
699 if (fpu_debug_level & DL_STOREEA) {
700 printf(" calc_ea: fetched ptr 0x%08x\n", word);
701 }
702 ptr = (char *)word + ea->ea_outerdisp;
703 }
704 }
705
706 *eaddr = ptr;
707
708 return 0;
709 }
710