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