1 /* $NetBSD: fpu_calcea.c,v 1.20 2009/03/14 15:36:09 dsl Exp $ */
4 * Copyright (c) 1995 Gordon W. Ross
5 * portion Copyright (c) 1995 Ken Nakata
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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
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.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: fpu_calcea.c,v 1.20 2009/03/14 15:36:09 dsl Exp $");
37 #include <sys/param.h>
38 #include <sys/signal.h>
39 #include <sys/systm.h>
40 #include <machine/frame.h>
41 #include <m68k/m68k.h>
43 #include "fpu_emulate.h"
46 * Prototypes of static functions
48 static int decode_ea6(struct frame
*frame
, struct instruction
*insn
,
49 struct insn_ea
*ea
, int modreg
);
50 static int fetch_immed(struct frame
*frame
, struct instruction
*insn
,
52 static int fetch_disp(struct frame
*frame
, struct instruction
*insn
,
54 static int calc_ea(struct insn_ea
*ea
, char *ptr
, char **eaddr
);
57 * Helper routines for dealing with "effective address" values.
61 * Decode an effective address into internal form.
62 * Returns zero on success, else signal number.
65 fpu_decode_ea(struct frame
*frame
, struct instruction
*insn
, struct insn_ea
*ea
, int modreg
)
70 if (insn
->is_datasize
< 0) {
71 panic("decode_ea: called with uninitialized datasize");
77 /* Set the most common value here. */
78 ea
->ea_regnum
= 8 + (modreg
& 7);
80 if ((modreg
& 060) == 0) {
82 ea
->ea_regnum
= modreg
& 0xf;
83 ea
->ea_flags
= EA_DIRECT
;
85 printf("decode_ea: register direct reg=%d\n", ea
->ea_regnum
);
87 } else if ((modreg
& 077) == 074) {
89 ea
->ea_flags
= EA_IMMED
;
90 sig
= fetch_immed(frame
, insn
, &ea
->ea_immed
[0]);
92 printf("decode_ea: immediate size=%d\n", insn
->is_datasize
);
96 * rest of the address modes need to be separately
97 * handled for the LC040 and the others.
100 else if (frame
->f_format
== 4 && frame
->f_fmt4
.f_fa
) {
102 ea
->ea_flags
= EA_FRAME_EA
;
103 ea
->ea_fea
= frame
->f_fmt4
.f_fa
;
105 printf("decode_ea: 68LC040 - in-frame EA (%p) size %d\n",
106 (void *)ea
->ea_fea
, insn
->is_datasize
);
108 if ((modreg
& 070) == 030) {
109 /* postincrement mode */
110 ea
->ea_flags
|= EA_POSTINCR
;
111 } else if ((modreg
& 070) == 040) {
112 /* predecrement mode */
113 ea
->ea_flags
|= EA_PREDECR
;
115 #if defined(M68020) || defined(M68030) || defined(M68040)
116 if (cputype
== CPU_68060
)
118 if (insn
->is_datasize
== 12)
126 switch (modreg
& 070) {
131 printf("decode_ea: register indirect reg=%d\n", ea
->ea_regnum
);
135 case 030: /* (An)+ */
136 ea
->ea_flags
= EA_POSTINCR
;
138 printf("decode_ea: reg indirect postincrement reg=%d\n",
143 case 040: /* -(An) */
144 ea
->ea_flags
= EA_PREDECR
;
146 printf("decode_ea: reg indirect predecrement reg=%d\n",
151 case 050: /* (d16,An) */
152 ea
->ea_flags
= EA_OFFSET
;
153 sig
= fetch_disp(frame
, insn
, 1, &ea
->ea_offset
);
155 printf("decode_ea: reg indirect with displacement reg=%d\n",
160 case 060: /* (d8,An,Xn) */
161 ea
->ea_flags
= EA_INDEXED
;
162 sig
= decode_ea6(frame
, insn
, ea
, modreg
);
165 case 070: /* misc. */
166 ea
->ea_regnum
= (modreg
& 7);
167 switch (modreg
& 7) {
169 case 0: /* (xxxx).W */
170 ea
->ea_flags
= EA_ABS
;
171 sig
= fetch_disp(frame
, insn
, 1, &ea
->ea_absaddr
);
173 printf("decode_ea: absolute address (word)\n");
177 case 1: /* (xxxxxxxx).L */
178 ea
->ea_flags
= EA_ABS
;
179 sig
= fetch_disp(frame
, insn
, 2, &ea
->ea_absaddr
);
181 printf("decode_ea: absolute address (long)\n");
185 case 2: /* (d16,PC) */
186 ea
->ea_flags
= EA_PC_REL
| EA_OFFSET
;
187 sig
= fetch_disp(frame
, insn
, 1, &ea
->ea_absaddr
);
189 printf("decode_ea: pc relative word displacement\n");
193 case 3: /* (d8,PC,Xn) */
194 ea
->ea_flags
= EA_PC_REL
| EA_INDEXED
;
195 sig
= decode_ea6(frame
, insn
, ea
, modreg
);
199 /* it should have been taken care of earlier */
202 printf("decode_ea: invalid addr mode (7,%d)\n", modreg
& 7);
205 } /* switch for mode 7 */
215 * Decode Mode=6 address modes
218 decode_ea6(struct frame
*frame
, struct instruction
*insn
, struct insn_ea
*ea
, int modreg
)
221 int basedisp
, outerdisp
;
222 int bd_size
, od_size
;
225 extword
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
229 insn
->is_advance
+= 2;
231 /* get register index */
232 ea
->ea_idxreg
= (extword
>> 12) & 0xf;
233 idx
= frame
->f_regs
[ea
->ea_idxreg
];
234 if ((extword
& 0x0800) == 0) {
235 /* if word sized index, sign-extend */
241 /* scale register index */
242 idx
<<= ((extword
>>9) & 3);
244 if ((extword
& 0x100) == 0) {
245 /* brief extension word - sign-extend the displacement */
246 basedisp
= (extword
& 0xff);
247 if (basedisp
& 0x80) {
248 basedisp
|= 0xffffff00;
251 ea
->ea_basedisp
= idx
+ basedisp
;
252 ea
->ea_outerdisp
= 0;
254 printf("decode_ea6: brief ext word idxreg=%d, basedisp=%08x\n",
255 ea
->ea_idxreg
, ea
->ea_basedisp
);
258 /* full extension word */
259 if (extword
& 0x80) {
260 ea
->ea_flags
|= EA_BASE_SUPPRSS
;
262 bd_size
= ((extword
>> 4) & 3) - 1;
263 od_size
= (extword
& 3) - 1;
264 sig
= fetch_disp(frame
, insn
, bd_size
, &basedisp
);
269 ea
->ea_flags
|= EA_MEM_INDIR
;
271 sig
= fetch_disp(frame
, insn
, od_size
, &outerdisp
);
276 switch (extword
& 0x44) {
277 case 0: /* preindexed */
278 ea
->ea_basedisp
= basedisp
+ idx
;
279 ea
->ea_outerdisp
= outerdisp
;
281 case 4: /* postindexed */
282 ea
->ea_basedisp
= basedisp
;
283 ea
->ea_outerdisp
= outerdisp
+ idx
;
285 case 0x40: /* no index */
286 ea
->ea_basedisp
= basedisp
;
287 ea
->ea_outerdisp
= outerdisp
;
291 printf("decode_ea6: invalid indirect mode: ext word %04x\n",
298 printf("decode_ea6: full ext idxreg=%d, basedisp=%x, outerdisp=%x\n",
299 ea
->ea_idxreg
, ea
->ea_basedisp
, ea
->ea_outerdisp
);
303 printf("decode_ea6: regnum=%d, flags=%x\n",
304 ea
->ea_regnum
, ea
->ea_flags
);
310 * Load a value from an effective address.
311 * Returns zero on success, else signal number.
314 fpu_load_ea(struct frame
*frame
, struct instruction
*insn
, struct insn_ea
*ea
, char *dst
)
322 if (ea
->ea_regnum
& ~0xF) {
323 panic("load_ea: bad regnum");
328 printf("load_ea: frame at %p\n", frame
);
330 /* dst is always int or larger. */
331 len
= insn
->is_datasize
;
335 step
= (len
== 1 && ea
->ea_regnum
== 15 /* sp */) ? 2 : len
;
338 if (ea
->ea_flags
& EA_FRAME_EA
) {
339 /* Using LC040 frame EA */
341 if (ea
->ea_flags
& (EA_PREDECR
|EA_POSTINCR
)) {
342 printf("load_ea: frame ea %08x w/r%d\n",
343 ea
->ea_fea
, ea
->ea_regnum
);
345 printf("load_ea: frame ea %08x\n", ea
->ea_fea
);
348 src
= (char *)ea
->ea_fea
;
349 copyin(src
+ ea
->ea_moffs
, dst
, len
);
350 if (ea
->ea_flags
& EA_PREDECR
) {
351 frame
->f_regs
[ea
->ea_regnum
] = ea
->ea_fea
;
354 } else if (ea
->ea_flags
& EA_POSTINCR
) {
356 frame
->f_regs
[ea
->ea_regnum
] = ea
->ea_fea
;
359 ea
->ea_moffs
+= step
;
361 /* That's it, folks */
364 if (ea
->ea_flags
& EA_DIRECT
) {
367 printf("load_ea: operand doesn't fit CPU reg\n");
371 if (ea
->ea_moffs
> 0) {
373 printf("load_ea: more than one move from CPU reg\n");
377 src
= (char *)&frame
->f_regs
[ea
->ea_regnum
];
378 /* The source is an int. */
382 printf("load_ea: short/byte opr - addr adjusted\n");
386 printf("load_ea: src %p\n", src
);
388 memcpy(dst
, src
, len
);
389 } else if (ea
->ea_flags
& EA_IMMED
) {
391 printf("load_ea: immed %08x%08x%08x size %d\n",
392 ea
->ea_immed
[0], ea
->ea_immed
[1], ea
->ea_immed
[2], len
);
394 src
= (char *)&ea
->ea_immed
[0];
398 printf("load_ea: short/byte immed opr - addr adjusted\n");
401 memcpy(dst
, src
, len
);
402 } else if (ea
->ea_flags
& EA_ABS
) {
404 printf("load_ea: abs addr %08x\n", ea
->ea_absaddr
);
406 src
= (char *)ea
->ea_absaddr
;
407 copyin(src
, dst
, len
);
408 } else /* register indirect */ {
409 if (ea
->ea_flags
& EA_PC_REL
) {
411 printf("load_ea: using PC\n");
414 /* Grab the register contents. 4 is offset to the first
415 extension word from the opcode */
416 src
= (char *)insn
->is_pc
+ 4;
418 printf("load_ea: pc relative pc+4 = %p\n", src
);
420 } else /* not PC relative */ {
422 printf("load_ea: using register %c%d\n",
423 (ea
->ea_regnum
>= 8) ? 'a' : 'd', ea
->ea_regnum
& 7);
425 /* point to the register */
426 reg
= &frame
->f_regs
[ea
->ea_regnum
];
428 if (ea
->ea_flags
& EA_PREDECR
) {
430 printf("load_ea: predecr mode - reg decremented\n");
436 /* Grab the register contents. */
439 printf("load_ea: reg indirect reg = %p\n", src
);
443 sig
= calc_ea(ea
, src
, &src
);
447 copyin(src
+ ea
->ea_moffs
, dst
, len
);
449 /* do post-increment */
450 if (ea
->ea_flags
& EA_POSTINCR
) {
451 if (ea
->ea_flags
& EA_PC_REL
) {
453 printf("load_ea: tried to postincrement PC\n");
460 printf("load_ea: postinc mode - reg incremented\n");
471 * Store a value at the effective address.
472 * Returns zero on success, else signal number.
475 fpu_store_ea(struct frame
*frame
, struct instruction
*insn
, struct insn_ea
*ea
, char *src
)
483 if (ea
->ea_regnum
& ~0xf) {
484 panic("store_ea: bad regnum");
488 if (ea
->ea_flags
& (EA_IMMED
|EA_PC_REL
)) {
489 /* not alterable address mode */
491 printf("store_ea: not alterable address mode\n");
496 /* src is always int or larger. */
497 len
= insn
->is_datasize
;
501 step
= (len
== 1 && ea
->ea_regnum
== 15 /* sp */) ? 2 : len
;
503 if (ea
->ea_flags
& EA_FRAME_EA
) {
504 /* Using LC040 frame EA */
506 if (ea
->ea_flags
& (EA_PREDECR
|EA_POSTINCR
)) {
507 printf("store_ea: frame ea %08x w/r%d\n",
508 ea
->ea_fea
, ea
->ea_regnum
);
510 printf("store_ea: frame ea %08x\n", ea
->ea_fea
);
513 dst
= (char *)ea
->ea_fea
;
514 copyout(src
, dst
+ ea
->ea_moffs
, len
);
515 if (ea
->ea_flags
& EA_PREDECR
) {
516 frame
->f_regs
[ea
->ea_regnum
] = ea
->ea_fea
;
519 } else if (ea
->ea_flags
& EA_POSTINCR
) {
521 frame
->f_regs
[ea
->ea_regnum
] = ea
->ea_fea
;
524 ea
->ea_moffs
+= step
;
526 /* That's it, folks */
527 } else if (ea
->ea_flags
& EA_ABS
) {
529 printf("store_ea: abs addr %08x\n", ea
->ea_absaddr
);
531 dst
= (char *)ea
->ea_absaddr
;
532 copyout(src
, dst
+ ea
->ea_moffs
, len
);
534 } else if (ea
->ea_flags
& EA_DIRECT
) {
537 printf("store_ea: operand doesn't fit CPU reg\n");
541 if (ea
->ea_moffs
> 0) {
543 printf("store_ea: more than one move to CPU reg\n");
547 dst
= (char*)&frame
->f_regs
[ea
->ea_regnum
];
548 /* The destination is an int. */
552 printf("store_ea: short/byte opr - dst addr adjusted\n");
556 printf("store_ea: dst %p\n", dst
);
558 memcpy(dst
, src
, len
);
559 } else /* One of MANY indirect forms... */ {
561 printf("store_ea: using register %c%d\n",
562 (ea
->ea_regnum
>= 8) ? 'a' : 'd', ea
->ea_regnum
& 7);
564 /* point to the register */
565 reg
= &(frame
->f_regs
[ea
->ea_regnum
]);
567 /* do pre-decrement */
568 if (ea
->ea_flags
& EA_PREDECR
) {
570 printf("store_ea: predecr mode - reg decremented\n");
576 /* calculate the effective address */
577 sig
= calc_ea(ea
, (char *)*reg
, &dst
);
582 printf("store_ea: dst addr=%p+%d\n", dst
, ea
->ea_moffs
);
584 copyout(src
, dst
+ ea
->ea_moffs
, len
);
586 /* do post-increment */
587 if (ea
->ea_flags
& EA_POSTINCR
) {
591 printf("store_ea: postinc mode - reg incremented\n");
602 * fetch_immed: fetch immediate operand
605 fetch_immed(struct frame
*frame
, struct instruction
*insn
, int *dst
)
609 ext_bytes
= insn
->is_datasize
;
612 data
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
616 if (ext_bytes
== 1) {
617 /* sign-extend byte to long */
622 } else if (ext_bytes
== 2) {
623 /* sign-extend word to long */
629 insn
->is_advance
+= 2;
633 data
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
637 insn
->is_advance
+= 2;
642 data
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
647 data
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
+ 2));
651 insn
->is_advance
+= 4;
655 data
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
660 data
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
+ 2));
664 insn
->is_advance
+= 4;
672 * fetch_disp: fetch displacement in full extension words
675 fetch_disp(struct frame
*frame
, struct instruction
*insn
, int size
, int *res
)
680 word
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
684 disp
= word
& 0xffff;
689 insn
->is_advance
+= 2;
690 } else if (size
== 2) {
691 word
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
696 word
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
+ 2));
700 disp
|= (word
& 0xffff);
701 insn
->is_advance
+= 4;
710 * Calculates an effective address for all address modes except for
711 * register direct, absolute, and immediate modes. However, it does
712 * not take care of predecrement/postincrement of register content.
713 * Returns a signal value (0 == no error).
716 calc_ea(struct insn_ea
*ea
, char *ptr
, char **eaddr
)
717 /* ptr: base address (usually a register content) */
718 /* eaddr: pointer to result pointer */
723 printf("calc_ea: reg indirect (reg) = %p\n", ptr
);
726 if (ea
->ea_flags
& EA_OFFSET
) {
727 /* apply the signed offset */
729 printf("calc_ea: offset %d\n", ea
->ea_offset
);
731 ptr
+= ea
->ea_offset
;
732 } else if (ea
->ea_flags
& EA_INDEXED
) {
734 printf("calc_ea: indexed mode\n");
737 if (ea
->ea_flags
& EA_BASE_SUPPRSS
) {
738 /* base register is suppressed */
739 ptr
= (char *)ea
->ea_basedisp
;
741 ptr
+= ea
->ea_basedisp
;
744 if (ea
->ea_flags
& EA_MEM_INDIR
) {
746 printf("calc_ea: mem indir mode: basedisp=%08x, outerdisp=%08x\n",
747 ea
->ea_basedisp
, ea
->ea_outerdisp
);
748 printf("calc_ea: addr fetched from %p\n", ptr
);
750 /* memory indirect modes */
756 data
= fusword(ptr
+ 2);
762 printf("calc_ea: fetched ptr 0x%08x\n", word
);
764 ptr
= (char *)word
+ ea
->ea_outerdisp
;