1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Linux/PA-RISC Project (http://www.parisc-linux.org/) |
4 | * |
5 | * Floating-point emulation code |
6 | * Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org> |
7 | */ |
8 | /* |
9 | * BEGIN_DESC |
10 | * |
11 | * File: |
12 | * @(#) pa/fp/fpudispatch.c $Revision: 1.1 $ |
13 | * |
14 | * Purpose: |
15 | * <<please update with a synopsis of the functionality provided by this file>> |
16 | * |
17 | * External Interfaces: |
18 | * <<the following list was autogenerated, please review>> |
19 | * emfpudispatch(ir, dummy1, dummy2, fpregs) |
20 | * fpudispatch(ir, excp_code, holder, fpregs) |
21 | * |
22 | * Internal Interfaces: |
23 | * <<the following list was autogenerated, please review>> |
24 | * static u_int decode_06(u_int, u_int *) |
25 | * static u_int decode_0c(u_int, u_int, u_int, u_int *) |
26 | * static u_int decode_0e(u_int, u_int, u_int, u_int *) |
27 | * static u_int decode_26(u_int, u_int *) |
28 | * static u_int decode_2e(u_int, u_int *) |
29 | * static void update_status_cbit(u_int *, u_int, u_int, u_int) |
30 | * |
31 | * Theory: |
32 | * <<please update with a overview of the operation of this file>> |
33 | * |
34 | * END_DESC |
35 | */ |
36 | |
37 | #define FPUDEBUG 0 |
38 | |
39 | #include "float.h" |
40 | #include <linux/bug.h> |
41 | #include <linux/kernel.h> |
42 | #include <asm/processor.h> |
43 | /* #include <sys/debug.h> */ |
44 | /* #include <machine/sys/mdep_private.h> */ |
45 | |
46 | #define COPR_INST 0x30000000 |
47 | |
48 | /* |
49 | * definition of extru macro. If pos and len are constants, the compiler |
50 | * will generate an extru instruction when optimized |
51 | */ |
52 | #define extru(r,pos,len) (((r) >> (31-(pos))) & (( 1 << (len)) - 1)) |
53 | /* definitions of bit field locations in the instruction */ |
54 | #define fpmajorpos 5 |
55 | #define fpr1pos 10 |
56 | #define fpr2pos 15 |
57 | #define fptpos 31 |
58 | #define fpsubpos 18 |
59 | #define fpclass1subpos 16 |
60 | #define fpclasspos 22 |
61 | #define fpfmtpos 20 |
62 | #define fpdfpos 18 |
63 | #define fpnulpos 26 |
64 | /* |
65 | * the following are the extra bits for the 0E major op |
66 | */ |
67 | #define fpxr1pos 24 |
68 | #define fpxr2pos 19 |
69 | #define fpxtpos 25 |
70 | #define fpxpos 23 |
71 | #define fp0efmtpos 20 |
72 | /* |
73 | * the following are for the multi-ops |
74 | */ |
75 | #define fprm1pos 10 |
76 | #define fprm2pos 15 |
77 | #define fptmpos 31 |
78 | #define fprapos 25 |
79 | #define fptapos 20 |
80 | #define fpmultifmt 26 |
81 | /* |
82 | * the following are for the fused FP instructions |
83 | */ |
84 | /* fprm1pos 10 */ |
85 | /* fprm2pos 15 */ |
86 | #define fpraupos 18 |
87 | #define fpxrm2pos 19 |
88 | /* fpfmtpos 20 */ |
89 | #define fpralpos 23 |
90 | #define fpxrm1pos 24 |
91 | /* fpxtpos 25 */ |
92 | #define fpfusedsubop 26 |
93 | /* fptpos 31 */ |
94 | |
95 | /* |
96 | * offset to constant zero in the FP emulation registers |
97 | */ |
98 | #define fpzeroreg (32*sizeof(double)/sizeof(u_int)) |
99 | |
100 | /* |
101 | * extract the major opcode from the instruction |
102 | */ |
103 | #define get_major(op) extru(op,fpmajorpos,6) |
104 | /* |
105 | * extract the two bit class field from the FP instruction. The class is at bit |
106 | * positions 21-22 |
107 | */ |
108 | #define get_class(op) extru(op,fpclasspos,2) |
109 | /* |
110 | * extract the 3 bit subop field. For all but class 1 instructions, it is |
111 | * located at bit positions 16-18 |
112 | */ |
113 | #define get_subop(op) extru(op,fpsubpos,3) |
114 | /* |
115 | * extract the 2 or 3 bit subop field from class 1 instructions. It is located |
116 | * at bit positions 15-16 (PA1.1) or 14-16 (PA2.0) |
117 | */ |
118 | #define get_subop1_PA1_1(op) extru(op,fpclass1subpos,2) /* PA89 (1.1) fmt */ |
119 | #define get_subop1_PA2_0(op) extru(op,fpclass1subpos,3) /* PA 2.0 fmt */ |
120 | |
121 | /* definitions of unimplemented exceptions */ |
122 | #define MAJOR_0C_EXCP 0x09 |
123 | #define MAJOR_0E_EXCP 0x0b |
124 | #define MAJOR_06_EXCP 0x03 |
125 | #define MAJOR_26_EXCP 0x23 |
126 | #define MAJOR_2E_EXCP 0x2b |
127 | #define PA83_UNIMP_EXCP 0x01 |
128 | |
129 | /* |
130 | * Special Defines for TIMEX specific code |
131 | */ |
132 | |
133 | #define FPU_TYPE_FLAG_POS (EM_FPU_TYPE_OFFSET>>2) |
134 | #define TIMEX_ROLEX_FPU_MASK (TIMEX_EXTEN_FLAG|ROLEX_EXTEN_FLAG) |
135 | |
136 | /* |
137 | * Static function definitions |
138 | */ |
139 | #define _PROTOTYPES |
140 | #if defined(_PROTOTYPES) || defined(_lint) |
141 | static u_int decode_0c(u_int, u_int, u_int, u_int *); |
142 | static u_int decode_0e(u_int, u_int, u_int, u_int *); |
143 | static u_int decode_06(u_int, u_int *); |
144 | static u_int decode_26(u_int, u_int *); |
145 | static u_int decode_2e(u_int, u_int *); |
146 | static void update_status_cbit(u_int *, u_int, u_int, u_int); |
147 | #else /* !_PROTOTYPES&&!_lint */ |
148 | static u_int decode_0c(); |
149 | static u_int decode_0e(); |
150 | static u_int decode_06(); |
151 | static u_int decode_26(); |
152 | static u_int decode_2e(); |
153 | static void update_status_cbit(); |
154 | #endif /* _PROTOTYPES&&!_lint */ |
155 | |
156 | #define VASSERT(x) |
157 | |
158 | static void parisc_linux_get_fpu_type(u_int fpregs[]) |
159 | { |
160 | /* on pa-linux the fpu type is not filled in by the |
161 | * caller; it is constructed here |
162 | */ |
163 | if (boot_cpu_data.cpu_type == pcxs) |
164 | fpregs[FPU_TYPE_FLAG_POS] = TIMEX_EXTEN_FLAG; |
165 | else if (boot_cpu_data.cpu_type == pcxt || |
166 | boot_cpu_data.cpu_type == pcxt_) |
167 | fpregs[FPU_TYPE_FLAG_POS] = ROLEX_EXTEN_FLAG; |
168 | else if (boot_cpu_data.cpu_type >= pcxu) |
169 | fpregs[FPU_TYPE_FLAG_POS] = PA2_0_FPU_FLAG; |
170 | } |
171 | |
172 | /* |
173 | * this routine will decode the excepting floating point instruction and |
174 | * call the appropriate emulation routine. |
175 | * It is called by decode_fpu with the following parameters: |
176 | * fpudispatch(current_ir, unimplemented_code, 0, &Fpu_register) |
177 | * where current_ir is the instruction to be emulated, |
178 | * unimplemented_code is the exception_code that the hardware generated |
179 | * and &Fpu_register is the address of emulated FP reg 0. |
180 | */ |
181 | u_int |
182 | fpudispatch(u_int ir, u_int excp_code, u_int holder, u_int fpregs[]) |
183 | { |
184 | u_int class, subop; |
185 | u_int fpu_type_flags; |
186 | |
187 | /* All FP emulation code assumes that ints are 4-bytes in length */ |
188 | VASSERT(sizeof(int) == 4); |
189 | |
190 | parisc_linux_get_fpu_type(fpregs); |
191 | |
192 | fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; /* get fpu type flags */ |
193 | |
194 | class = get_class(ir); |
195 | if (class == 1) { |
196 | if (fpu_type_flags & PA2_0_FPU_FLAG) |
197 | subop = get_subop1_PA2_0(ir); |
198 | else |
199 | subop = get_subop1_PA1_1(ir); |
200 | } |
201 | else |
202 | subop = get_subop(ir); |
203 | |
204 | if (FPUDEBUG) printk("class %d subop %d\n" , class, subop); |
205 | |
206 | switch (excp_code) { |
207 | case MAJOR_0C_EXCP: |
208 | case PA83_UNIMP_EXCP: |
209 | return(decode_0c(ir,class,subop,fpregs)); |
210 | case MAJOR_0E_EXCP: |
211 | return(decode_0e(ir,class,subop,fpregs)); |
212 | case MAJOR_06_EXCP: |
213 | return(decode_06(ir,fpregs)); |
214 | case MAJOR_26_EXCP: |
215 | return(decode_26(ir,fpregs)); |
216 | case MAJOR_2E_EXCP: |
217 | return(decode_2e(ir,fpregs)); |
218 | default: |
219 | /* "crashme Night Gallery painting nr 2. (asm_crash.s). |
220 | * This was fixed for multi-user kernels, but |
221 | * workstation kernels had a panic here. This allowed |
222 | * any arbitrary user to panic the kernel by executing |
223 | * setting the FP exception registers to strange values |
224 | * and generating an emulation trap. The emulation and |
225 | * exception code must never be able to panic the |
226 | * kernel. |
227 | */ |
228 | return(UNIMPLEMENTEDEXCEPTION); |
229 | } |
230 | } |
231 | |
232 | /* |
233 | * this routine is called by $emulation_trap to emulate a coprocessor |
234 | * instruction if one doesn't exist |
235 | */ |
236 | u_int |
237 | emfpudispatch(u_int ir, u_int dummy1, u_int dummy2, u_int fpregs[]) |
238 | { |
239 | u_int class, subop, major; |
240 | u_int fpu_type_flags; |
241 | |
242 | /* All FP emulation code assumes that ints are 4-bytes in length */ |
243 | VASSERT(sizeof(int) == 4); |
244 | |
245 | fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; /* get fpu type flags */ |
246 | |
247 | major = get_major(ir); |
248 | class = get_class(ir); |
249 | if (class == 1) { |
250 | if (fpu_type_flags & PA2_0_FPU_FLAG) |
251 | subop = get_subop1_PA2_0(ir); |
252 | else |
253 | subop = get_subop1_PA1_1(ir); |
254 | } |
255 | else |
256 | subop = get_subop(ir); |
257 | switch (major) { |
258 | case 0x0C: |
259 | return(decode_0c(ir,class,subop,fpregs)); |
260 | case 0x0E: |
261 | return(decode_0e(ir,class,subop,fpregs)); |
262 | case 0x06: |
263 | return(decode_06(ir,fpregs)); |
264 | case 0x26: |
265 | return(decode_26(ir,fpregs)); |
266 | case 0x2E: |
267 | return(decode_2e(ir,fpregs)); |
268 | default: |
269 | return(PA83_UNIMP_EXCP); |
270 | } |
271 | } |
272 | |
273 | |
274 | static u_int |
275 | decode_0c(u_int ir, u_int class, u_int subop, u_int fpregs[]) |
276 | { |
277 | u_int r1,r2,t; /* operand register offsets */ |
278 | u_int fmt; /* also sf for class 1 conversions */ |
279 | u_int df; /* for class 1 conversions */ |
280 | u_int *status; |
281 | u_int retval, local_status; |
282 | u_int fpu_type_flags; |
283 | |
284 | if (ir == COPR_INST) { |
285 | fpregs[0] = EMULATION_VERSION << 11; |
286 | return(NOEXCEPTION); |
287 | } |
288 | status = &fpregs[0]; /* fp status register */ |
289 | local_status = fpregs[0]; /* and local copy */ |
290 | r1 = extru(ir,fpr1pos,5) * sizeof(double)/sizeof(u_int); |
291 | if (r1 == 0) /* map fr0 source to constant zero */ |
292 | r1 = fpzeroreg; |
293 | t = extru(ir,fptpos,5) * sizeof(double)/sizeof(u_int); |
294 | if (t == 0 && class != 2) /* don't allow fr0 as a dest */ |
295 | return(MAJOR_0C_EXCP); |
296 | fmt = extru(ir,fpfmtpos,2); /* get fmt completer */ |
297 | |
298 | switch (class) { |
299 | case 0: |
300 | switch (subop) { |
301 | case 0: /* COPR 0,0 emulated above*/ |
302 | case 1: |
303 | return(MAJOR_0C_EXCP); |
304 | case 2: /* FCPY */ |
305 | switch (fmt) { |
306 | case 2: /* illegal */ |
307 | return(MAJOR_0C_EXCP); |
308 | case 3: /* quad */ |
309 | t &= ~3; /* force to even reg #s */ |
310 | r1 &= ~3; |
311 | fpregs[t+3] = fpregs[r1+3]; |
312 | fpregs[t+2] = fpregs[r1+2]; |
313 | fallthrough; |
314 | case 1: /* double */ |
315 | fpregs[t+1] = fpregs[r1+1]; |
316 | fallthrough; |
317 | case 0: /* single */ |
318 | fpregs[t] = fpregs[r1]; |
319 | return(NOEXCEPTION); |
320 | } |
321 | BUG(); |
322 | case 3: /* FABS */ |
323 | switch (fmt) { |
324 | case 2: /* illegal */ |
325 | return(MAJOR_0C_EXCP); |
326 | case 3: /* quad */ |
327 | t &= ~3; /* force to even reg #s */ |
328 | r1 &= ~3; |
329 | fpregs[t+3] = fpregs[r1+3]; |
330 | fpregs[t+2] = fpregs[r1+2]; |
331 | fallthrough; |
332 | case 1: /* double */ |
333 | fpregs[t+1] = fpregs[r1+1]; |
334 | fallthrough; |
335 | case 0: /* single */ |
336 | /* copy and clear sign bit */ |
337 | fpregs[t] = fpregs[r1] & 0x7fffffff; |
338 | return(NOEXCEPTION); |
339 | } |
340 | BUG(); |
341 | case 6: /* FNEG */ |
342 | switch (fmt) { |
343 | case 2: /* illegal */ |
344 | return(MAJOR_0C_EXCP); |
345 | case 3: /* quad */ |
346 | t &= ~3; /* force to even reg #s */ |
347 | r1 &= ~3; |
348 | fpregs[t+3] = fpregs[r1+3]; |
349 | fpregs[t+2] = fpregs[r1+2]; |
350 | fallthrough; |
351 | case 1: /* double */ |
352 | fpregs[t+1] = fpregs[r1+1]; |
353 | fallthrough; |
354 | case 0: /* single */ |
355 | /* copy and invert sign bit */ |
356 | fpregs[t] = fpregs[r1] ^ 0x80000000; |
357 | return(NOEXCEPTION); |
358 | } |
359 | BUG(); |
360 | case 7: /* FNEGABS */ |
361 | switch (fmt) { |
362 | case 2: /* illegal */ |
363 | return(MAJOR_0C_EXCP); |
364 | case 3: /* quad */ |
365 | t &= ~3; /* force to even reg #s */ |
366 | r1 &= ~3; |
367 | fpregs[t+3] = fpregs[r1+3]; |
368 | fpregs[t+2] = fpregs[r1+2]; |
369 | fallthrough; |
370 | case 1: /* double */ |
371 | fpregs[t+1] = fpregs[r1+1]; |
372 | fallthrough; |
373 | case 0: /* single */ |
374 | /* copy and set sign bit */ |
375 | fpregs[t] = fpregs[r1] | 0x80000000; |
376 | return(NOEXCEPTION); |
377 | } |
378 | BUG(); |
379 | case 4: /* FSQRT */ |
380 | switch (fmt) { |
381 | case 0: |
382 | return(sgl_fsqrt(&fpregs[r1],0, |
383 | &fpregs[t],status)); |
384 | case 1: |
385 | return(dbl_fsqrt(&fpregs[r1],0, |
386 | &fpregs[t],status)); |
387 | case 2: |
388 | case 3: /* quad not implemented */ |
389 | return(MAJOR_0C_EXCP); |
390 | } |
391 | BUG(); |
392 | case 5: /* FRND */ |
393 | switch (fmt) { |
394 | case 0: |
395 | return(sgl_frnd(&fpregs[r1],0, |
396 | &fpregs[t],status)); |
397 | case 1: |
398 | return(dbl_frnd(&fpregs[r1],0, |
399 | &fpregs[t],status)); |
400 | case 2: |
401 | case 3: /* quad not implemented */ |
402 | return(MAJOR_0C_EXCP); |
403 | } |
404 | } /* end of switch (subop) */ |
405 | BUG(); |
406 | case 1: /* class 1 */ |
407 | df = extru(ir,fpdfpos,2); /* get dest format */ |
408 | if ((df & 2) || (fmt & 2)) { |
409 | /* |
410 | * fmt's 2 and 3 are illegal of not implemented |
411 | * quad conversions |
412 | */ |
413 | return(MAJOR_0C_EXCP); |
414 | } |
415 | /* |
416 | * encode source and dest formats into 2 bits. |
417 | * high bit is source, low bit is dest. |
418 | * bit = 1 --> double precision |
419 | */ |
420 | fmt = (fmt << 1) | df; |
421 | switch (subop) { |
422 | case 0: /* FCNVFF */ |
423 | switch(fmt) { |
424 | case 0: /* sgl/sgl */ |
425 | return(MAJOR_0C_EXCP); |
426 | case 1: /* sgl/dbl */ |
427 | return(sgl_to_dbl_fcnvff(&fpregs[r1],0, |
428 | &fpregs[t],status)); |
429 | case 2: /* dbl/sgl */ |
430 | return(dbl_to_sgl_fcnvff(&fpregs[r1],0, |
431 | &fpregs[t],status)); |
432 | case 3: /* dbl/dbl */ |
433 | return(MAJOR_0C_EXCP); |
434 | } |
435 | BUG(); |
436 | case 1: /* FCNVXF */ |
437 | switch(fmt) { |
438 | case 0: /* sgl/sgl */ |
439 | return(sgl_to_sgl_fcnvxf(&fpregs[r1],0, |
440 | &fpregs[t],status)); |
441 | case 1: /* sgl/dbl */ |
442 | return(sgl_to_dbl_fcnvxf(&fpregs[r1],0, |
443 | &fpregs[t],status)); |
444 | case 2: /* dbl/sgl */ |
445 | return(dbl_to_sgl_fcnvxf(&fpregs[r1],0, |
446 | &fpregs[t],status)); |
447 | case 3: /* dbl/dbl */ |
448 | return(dbl_to_dbl_fcnvxf(&fpregs[r1],0, |
449 | &fpregs[t],status)); |
450 | } |
451 | BUG(); |
452 | case 2: /* FCNVFX */ |
453 | switch(fmt) { |
454 | case 0: /* sgl/sgl */ |
455 | return(sgl_to_sgl_fcnvfx(&fpregs[r1],0, |
456 | &fpregs[t],status)); |
457 | case 1: /* sgl/dbl */ |
458 | return(sgl_to_dbl_fcnvfx(&fpregs[r1],0, |
459 | &fpregs[t],status)); |
460 | case 2: /* dbl/sgl */ |
461 | return(dbl_to_sgl_fcnvfx(&fpregs[r1],0, |
462 | &fpregs[t],status)); |
463 | case 3: /* dbl/dbl */ |
464 | return(dbl_to_dbl_fcnvfx(&fpregs[r1],0, |
465 | &fpregs[t],status)); |
466 | } |
467 | BUG(); |
468 | case 3: /* FCNVFXT */ |
469 | switch(fmt) { |
470 | case 0: /* sgl/sgl */ |
471 | return(sgl_to_sgl_fcnvfxt(&fpregs[r1],0, |
472 | &fpregs[t],status)); |
473 | case 1: /* sgl/dbl */ |
474 | return(sgl_to_dbl_fcnvfxt(&fpregs[r1],0, |
475 | &fpregs[t],status)); |
476 | case 2: /* dbl/sgl */ |
477 | return(dbl_to_sgl_fcnvfxt(&fpregs[r1],0, |
478 | &fpregs[t],status)); |
479 | case 3: /* dbl/dbl */ |
480 | return(dbl_to_dbl_fcnvfxt(&fpregs[r1],0, |
481 | &fpregs[t],status)); |
482 | } |
483 | BUG(); |
484 | case 5: /* FCNVUF (PA2.0 only) */ |
485 | switch(fmt) { |
486 | case 0: /* sgl/sgl */ |
487 | return(sgl_to_sgl_fcnvuf(&fpregs[r1],0, |
488 | &fpregs[t],status)); |
489 | case 1: /* sgl/dbl */ |
490 | return(sgl_to_dbl_fcnvuf(&fpregs[r1],0, |
491 | &fpregs[t],status)); |
492 | case 2: /* dbl/sgl */ |
493 | return(dbl_to_sgl_fcnvuf(&fpregs[r1],0, |
494 | &fpregs[t],status)); |
495 | case 3: /* dbl/dbl */ |
496 | return(dbl_to_dbl_fcnvuf(&fpregs[r1],0, |
497 | &fpregs[t],status)); |
498 | } |
499 | BUG(); |
500 | case 6: /* FCNVFU (PA2.0 only) */ |
501 | switch(fmt) { |
502 | case 0: /* sgl/sgl */ |
503 | return(sgl_to_sgl_fcnvfu(&fpregs[r1],0, |
504 | &fpregs[t],status)); |
505 | case 1: /* sgl/dbl */ |
506 | return(sgl_to_dbl_fcnvfu(&fpregs[r1],0, |
507 | &fpregs[t],status)); |
508 | case 2: /* dbl/sgl */ |
509 | return(dbl_to_sgl_fcnvfu(&fpregs[r1],0, |
510 | &fpregs[t],status)); |
511 | case 3: /* dbl/dbl */ |
512 | return(dbl_to_dbl_fcnvfu(&fpregs[r1],0, |
513 | &fpregs[t],status)); |
514 | } |
515 | BUG(); |
516 | case 7: /* FCNVFUT (PA2.0 only) */ |
517 | switch(fmt) { |
518 | case 0: /* sgl/sgl */ |
519 | return(sgl_to_sgl_fcnvfut(&fpregs[r1],0, |
520 | &fpregs[t],status)); |
521 | case 1: /* sgl/dbl */ |
522 | return(sgl_to_dbl_fcnvfut(&fpregs[r1],0, |
523 | &fpregs[t],status)); |
524 | case 2: /* dbl/sgl */ |
525 | return(dbl_to_sgl_fcnvfut(&fpregs[r1],0, |
526 | &fpregs[t],status)); |
527 | case 3: /* dbl/dbl */ |
528 | return(dbl_to_dbl_fcnvfut(&fpregs[r1],0, |
529 | &fpregs[t],status)); |
530 | } |
531 | BUG(); |
532 | case 4: /* undefined */ |
533 | return(MAJOR_0C_EXCP); |
534 | } /* end of switch subop */ |
535 | BUG(); |
536 | case 2: /* class 2 */ |
537 | fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; |
538 | r2 = extru(ir, fpr2pos, 5) * sizeof(double)/sizeof(u_int); |
539 | if (r2 == 0) |
540 | r2 = fpzeroreg; |
541 | if (fpu_type_flags & PA2_0_FPU_FLAG) { |
542 | /* FTEST if nullify bit set, otherwise FCMP */ |
543 | if (extru(ir, fpnulpos, 1)) { /* FTEST */ |
544 | switch (fmt) { |
545 | case 0: |
546 | /* |
547 | * arg0 is not used |
548 | * second param is the t field used for |
549 | * ftest,acc and ftest,rej |
550 | * third param is the subop (y-field) |
551 | */ |
552 | BUG(); |
553 | /* Unsupported |
554 | * return(ftest(0L,extru(ir,fptpos,5), |
555 | * &fpregs[0],subop)); |
556 | */ |
557 | case 1: |
558 | case 2: |
559 | case 3: |
560 | return(MAJOR_0C_EXCP); |
561 | } |
562 | } else { /* FCMP */ |
563 | switch (fmt) { |
564 | case 0: |
565 | retval = sgl_fcmp(&fpregs[r1], |
566 | &fpregs[r2],extru(ir,fptpos,5), |
567 | &local_status); |
568 | update_status_cbit(status,local_status, |
569 | fpu_type_flags, subop); |
570 | return(retval); |
571 | case 1: |
572 | retval = dbl_fcmp(&fpregs[r1], |
573 | &fpregs[r2],extru(ir,fptpos,5), |
574 | &local_status); |
575 | update_status_cbit(status,local_status, |
576 | fpu_type_flags, subop); |
577 | return(retval); |
578 | case 2: /* illegal */ |
579 | case 3: /* quad not implemented */ |
580 | return(MAJOR_0C_EXCP); |
581 | } |
582 | } |
583 | } /* end of if for PA2.0 */ |
584 | else { /* PA1.0 & PA1.1 */ |
585 | switch (subop) { |
586 | case 2: |
587 | case 3: |
588 | case 4: |
589 | case 5: |
590 | case 6: |
591 | case 7: |
592 | return(MAJOR_0C_EXCP); |
593 | case 0: /* FCMP */ |
594 | switch (fmt) { |
595 | case 0: |
596 | retval = sgl_fcmp(&fpregs[r1], |
597 | &fpregs[r2],extru(ir,fptpos,5), |
598 | &local_status); |
599 | update_status_cbit(status,local_status, |
600 | fpu_type_flags, subop); |
601 | return(retval); |
602 | case 1: |
603 | retval = dbl_fcmp(&fpregs[r1], |
604 | &fpregs[r2],extru(ir,fptpos,5), |
605 | &local_status); |
606 | update_status_cbit(status,local_status, |
607 | fpu_type_flags, subop); |
608 | return(retval); |
609 | case 2: /* illegal */ |
610 | case 3: /* quad not implemented */ |
611 | return(MAJOR_0C_EXCP); |
612 | } |
613 | BUG(); |
614 | case 1: /* FTEST */ |
615 | switch (fmt) { |
616 | case 0: |
617 | /* |
618 | * arg0 is not used |
619 | * second param is the t field used for |
620 | * ftest,acc and ftest,rej |
621 | * third param is the subop (y-field) |
622 | */ |
623 | BUG(); |
624 | /* unsupported |
625 | * return(ftest(0L,extru(ir,fptpos,5), |
626 | * &fpregs[0],subop)); |
627 | */ |
628 | case 1: |
629 | case 2: |
630 | case 3: |
631 | return(MAJOR_0C_EXCP); |
632 | } |
633 | BUG(); |
634 | } /* end of switch subop */ |
635 | } /* end of else for PA1.0 & PA1.1 */ |
636 | BUG(); |
637 | case 3: /* class 3 */ |
638 | r2 = extru(ir,fpr2pos,5) * sizeof(double)/sizeof(u_int); |
639 | if (r2 == 0) |
640 | r2 = fpzeroreg; |
641 | switch (subop) { |
642 | case 5: |
643 | case 6: |
644 | case 7: |
645 | return(MAJOR_0C_EXCP); |
646 | |
647 | case 0: /* FADD */ |
648 | switch (fmt) { |
649 | case 0: |
650 | return(sgl_fadd(&fpregs[r1],&fpregs[r2], |
651 | &fpregs[t],status)); |
652 | case 1: |
653 | return(dbl_fadd(&fpregs[r1],&fpregs[r2], |
654 | &fpregs[t],status)); |
655 | case 2: /* illegal */ |
656 | case 3: /* quad not implemented */ |
657 | return(MAJOR_0C_EXCP); |
658 | } |
659 | BUG(); |
660 | case 1: /* FSUB */ |
661 | switch (fmt) { |
662 | case 0: |
663 | return(sgl_fsub(&fpregs[r1],&fpregs[r2], |
664 | &fpregs[t],status)); |
665 | case 1: |
666 | return(dbl_fsub(&fpregs[r1],&fpregs[r2], |
667 | &fpregs[t],status)); |
668 | case 2: /* illegal */ |
669 | case 3: /* quad not implemented */ |
670 | return(MAJOR_0C_EXCP); |
671 | } |
672 | BUG(); |
673 | case 2: /* FMPY */ |
674 | switch (fmt) { |
675 | case 0: |
676 | return(sgl_fmpy(&fpregs[r1],&fpregs[r2], |
677 | &fpregs[t],status)); |
678 | case 1: |
679 | return(dbl_fmpy(&fpregs[r1],&fpregs[r2], |
680 | &fpregs[t],status)); |
681 | case 2: /* illegal */ |
682 | case 3: /* quad not implemented */ |
683 | return(MAJOR_0C_EXCP); |
684 | } |
685 | BUG(); |
686 | case 3: /* FDIV */ |
687 | switch (fmt) { |
688 | case 0: |
689 | return(sgl_fdiv(&fpregs[r1],&fpregs[r2], |
690 | &fpregs[t],status)); |
691 | case 1: |
692 | return(dbl_fdiv(&fpregs[r1],&fpregs[r2], |
693 | &fpregs[t],status)); |
694 | case 2: /* illegal */ |
695 | case 3: /* quad not implemented */ |
696 | return(MAJOR_0C_EXCP); |
697 | } |
698 | BUG(); |
699 | case 4: /* FREM */ |
700 | switch (fmt) { |
701 | case 0: |
702 | return(sgl_frem(&fpregs[r1],&fpregs[r2], |
703 | &fpregs[t],status)); |
704 | case 1: |
705 | return(dbl_frem(&fpregs[r1],&fpregs[r2], |
706 | &fpregs[t],status)); |
707 | case 2: /* illegal */ |
708 | case 3: /* quad not implemented */ |
709 | return(MAJOR_0C_EXCP); |
710 | } |
711 | BUG(); |
712 | } /* end of class 3 switch */ |
713 | } /* end of switch(class) */ |
714 | |
715 | /* If we get here, something is really wrong! */ |
716 | return(MAJOR_0C_EXCP); |
717 | } |
718 | |
719 | static u_int |
720 | decode_0e(ir,class,subop,fpregs) |
721 | u_int ir,class,subop; |
722 | u_int fpregs[]; |
723 | { |
724 | u_int r1,r2,t; /* operand register offsets */ |
725 | u_int fmt; /* also sf for class 1 conversions */ |
726 | u_int df; /* dest format for class 1 conversions */ |
727 | u_int *status; |
728 | u_int retval, local_status; |
729 | u_int fpu_type_flags; |
730 | |
731 | status = &fpregs[0]; |
732 | local_status = fpregs[0]; |
733 | r1 = ((extru(ir,fpr1pos,5)<<1)|(extru(ir,fpxr1pos,1))); |
734 | if (r1 == 0) |
735 | r1 = fpzeroreg; |
736 | t = ((extru(ir,fptpos,5)<<1)|(extru(ir,fpxtpos,1))); |
737 | if (t == 0 && class != 2) |
738 | return(MAJOR_0E_EXCP); |
739 | if (class < 2) /* class 0 or 1 has 2 bit fmt */ |
740 | fmt = extru(ir,fpfmtpos,2); |
741 | else /* class 2 and 3 have 1 bit fmt */ |
742 | fmt = extru(ir,fp0efmtpos,1); |
743 | /* |
744 | * An undefined combination, double precision accessing the |
745 | * right half of a FPR, can get us into trouble. |
746 | * Let's just force proper alignment on it. |
747 | */ |
748 | if (fmt == DBL) { |
749 | r1 &= ~1; |
750 | if (class != 1) |
751 | t &= ~1; |
752 | } |
753 | |
754 | switch (class) { |
755 | case 0: |
756 | switch (subop) { |
757 | case 0: /* unimplemented */ |
758 | case 1: |
759 | return(MAJOR_0E_EXCP); |
760 | case 2: /* FCPY */ |
761 | switch (fmt) { |
762 | case 2: |
763 | case 3: |
764 | return(MAJOR_0E_EXCP); |
765 | case 1: /* double */ |
766 | fpregs[t+1] = fpregs[r1+1]; |
767 | fallthrough; |
768 | case 0: /* single */ |
769 | fpregs[t] = fpregs[r1]; |
770 | return(NOEXCEPTION); |
771 | } |
772 | BUG(); |
773 | case 3: /* FABS */ |
774 | switch (fmt) { |
775 | case 2: |
776 | case 3: |
777 | return(MAJOR_0E_EXCP); |
778 | case 1: /* double */ |
779 | fpregs[t+1] = fpregs[r1+1]; |
780 | fallthrough; |
781 | case 0: /* single */ |
782 | fpregs[t] = fpregs[r1] & 0x7fffffff; |
783 | return(NOEXCEPTION); |
784 | } |
785 | BUG(); |
786 | case 6: /* FNEG */ |
787 | switch (fmt) { |
788 | case 2: |
789 | case 3: |
790 | return(MAJOR_0E_EXCP); |
791 | case 1: /* double */ |
792 | fpregs[t+1] = fpregs[r1+1]; |
793 | fallthrough; |
794 | case 0: /* single */ |
795 | fpregs[t] = fpregs[r1] ^ 0x80000000; |
796 | return(NOEXCEPTION); |
797 | } |
798 | BUG(); |
799 | case 7: /* FNEGABS */ |
800 | switch (fmt) { |
801 | case 2: |
802 | case 3: |
803 | return(MAJOR_0E_EXCP); |
804 | case 1: /* double */ |
805 | fpregs[t+1] = fpregs[r1+1]; |
806 | fallthrough; |
807 | case 0: /* single */ |
808 | fpregs[t] = fpregs[r1] | 0x80000000; |
809 | return(NOEXCEPTION); |
810 | } |
811 | BUG(); |
812 | case 4: /* FSQRT */ |
813 | switch (fmt) { |
814 | case 0: |
815 | return(sgl_fsqrt(&fpregs[r1],0, |
816 | &fpregs[t], status)); |
817 | case 1: |
818 | return(dbl_fsqrt(&fpregs[r1],0, |
819 | &fpregs[t], status)); |
820 | case 2: |
821 | case 3: |
822 | return(MAJOR_0E_EXCP); |
823 | } |
824 | BUG(); |
825 | case 5: /* FRMD */ |
826 | switch (fmt) { |
827 | case 0: |
828 | return(sgl_frnd(&fpregs[r1],0, |
829 | &fpregs[t], status)); |
830 | case 1: |
831 | return(dbl_frnd(&fpregs[r1],0, |
832 | &fpregs[t], status)); |
833 | case 2: |
834 | case 3: |
835 | return(MAJOR_0E_EXCP); |
836 | } |
837 | } /* end of switch (subop */ |
838 | BUG(); |
839 | case 1: /* class 1 */ |
840 | df = extru(ir,fpdfpos,2); /* get dest format */ |
841 | /* |
842 | * Fix Crashme problem (writing to 31R in double precision) |
843 | * here too. |
844 | */ |
845 | if (df == DBL) { |
846 | t &= ~1; |
847 | } |
848 | if ((df & 2) || (fmt & 2)) |
849 | return(MAJOR_0E_EXCP); |
850 | |
851 | fmt = (fmt << 1) | df; |
852 | switch (subop) { |
853 | case 0: /* FCNVFF */ |
854 | switch(fmt) { |
855 | case 0: /* sgl/sgl */ |
856 | return(MAJOR_0E_EXCP); |
857 | case 1: /* sgl/dbl */ |
858 | return(sgl_to_dbl_fcnvff(&fpregs[r1],0, |
859 | &fpregs[t],status)); |
860 | case 2: /* dbl/sgl */ |
861 | return(dbl_to_sgl_fcnvff(&fpregs[r1],0, |
862 | &fpregs[t],status)); |
863 | case 3: /* dbl/dbl */ |
864 | return(MAJOR_0E_EXCP); |
865 | } |
866 | BUG(); |
867 | case 1: /* FCNVXF */ |
868 | switch(fmt) { |
869 | case 0: /* sgl/sgl */ |
870 | return(sgl_to_sgl_fcnvxf(&fpregs[r1],0, |
871 | &fpregs[t],status)); |
872 | case 1: /* sgl/dbl */ |
873 | return(sgl_to_dbl_fcnvxf(&fpregs[r1],0, |
874 | &fpregs[t],status)); |
875 | case 2: /* dbl/sgl */ |
876 | return(dbl_to_sgl_fcnvxf(&fpregs[r1],0, |
877 | &fpregs[t],status)); |
878 | case 3: /* dbl/dbl */ |
879 | return(dbl_to_dbl_fcnvxf(&fpregs[r1],0, |
880 | &fpregs[t],status)); |
881 | } |
882 | BUG(); |
883 | case 2: /* FCNVFX */ |
884 | switch(fmt) { |
885 | case 0: /* sgl/sgl */ |
886 | return(sgl_to_sgl_fcnvfx(&fpregs[r1],0, |
887 | &fpregs[t],status)); |
888 | case 1: /* sgl/dbl */ |
889 | return(sgl_to_dbl_fcnvfx(&fpregs[r1],0, |
890 | &fpregs[t],status)); |
891 | case 2: /* dbl/sgl */ |
892 | return(dbl_to_sgl_fcnvfx(&fpregs[r1],0, |
893 | &fpregs[t],status)); |
894 | case 3: /* dbl/dbl */ |
895 | return(dbl_to_dbl_fcnvfx(&fpregs[r1],0, |
896 | &fpregs[t],status)); |
897 | } |
898 | BUG(); |
899 | case 3: /* FCNVFXT */ |
900 | switch(fmt) { |
901 | case 0: /* sgl/sgl */ |
902 | return(sgl_to_sgl_fcnvfxt(&fpregs[r1],0, |
903 | &fpregs[t],status)); |
904 | case 1: /* sgl/dbl */ |
905 | return(sgl_to_dbl_fcnvfxt(&fpregs[r1],0, |
906 | &fpregs[t],status)); |
907 | case 2: /* dbl/sgl */ |
908 | return(dbl_to_sgl_fcnvfxt(&fpregs[r1],0, |
909 | &fpregs[t],status)); |
910 | case 3: /* dbl/dbl */ |
911 | return(dbl_to_dbl_fcnvfxt(&fpregs[r1],0, |
912 | &fpregs[t],status)); |
913 | } |
914 | BUG(); |
915 | case 5: /* FCNVUF (PA2.0 only) */ |
916 | switch(fmt) { |
917 | case 0: /* sgl/sgl */ |
918 | return(sgl_to_sgl_fcnvuf(&fpregs[r1],0, |
919 | &fpregs[t],status)); |
920 | case 1: /* sgl/dbl */ |
921 | return(sgl_to_dbl_fcnvuf(&fpregs[r1],0, |
922 | &fpregs[t],status)); |
923 | case 2: /* dbl/sgl */ |
924 | return(dbl_to_sgl_fcnvuf(&fpregs[r1],0, |
925 | &fpregs[t],status)); |
926 | case 3: /* dbl/dbl */ |
927 | return(dbl_to_dbl_fcnvuf(&fpregs[r1],0, |
928 | &fpregs[t],status)); |
929 | } |
930 | BUG(); |
931 | case 6: /* FCNVFU (PA2.0 only) */ |
932 | switch(fmt) { |
933 | case 0: /* sgl/sgl */ |
934 | return(sgl_to_sgl_fcnvfu(&fpregs[r1],0, |
935 | &fpregs[t],status)); |
936 | case 1: /* sgl/dbl */ |
937 | return(sgl_to_dbl_fcnvfu(&fpregs[r1],0, |
938 | &fpregs[t],status)); |
939 | case 2: /* dbl/sgl */ |
940 | return(dbl_to_sgl_fcnvfu(&fpregs[r1],0, |
941 | &fpregs[t],status)); |
942 | case 3: /* dbl/dbl */ |
943 | return(dbl_to_dbl_fcnvfu(&fpregs[r1],0, |
944 | &fpregs[t],status)); |
945 | } |
946 | BUG(); |
947 | case 7: /* FCNVFUT (PA2.0 only) */ |
948 | switch(fmt) { |
949 | case 0: /* sgl/sgl */ |
950 | return(sgl_to_sgl_fcnvfut(&fpregs[r1],0, |
951 | &fpregs[t],status)); |
952 | case 1: /* sgl/dbl */ |
953 | return(sgl_to_dbl_fcnvfut(&fpregs[r1],0, |
954 | &fpregs[t],status)); |
955 | case 2: /* dbl/sgl */ |
956 | return(dbl_to_sgl_fcnvfut(&fpregs[r1],0, |
957 | &fpregs[t],status)); |
958 | case 3: /* dbl/dbl */ |
959 | return(dbl_to_dbl_fcnvfut(&fpregs[r1],0, |
960 | &fpregs[t],status)); |
961 | } |
962 | BUG(); |
963 | case 4: /* undefined */ |
964 | return(MAJOR_0C_EXCP); |
965 | } /* end of switch subop */ |
966 | BUG(); |
967 | case 2: /* class 2 */ |
968 | /* |
969 | * Be careful out there. |
970 | * Crashme can generate cases where FR31R is specified |
971 | * as the source or target of a double precision operation. |
972 | * Since we just pass the address of the floating-point |
973 | * register to the emulation routines, this can cause |
974 | * corruption of fpzeroreg. |
975 | */ |
976 | if (fmt == DBL) |
977 | r2 = (extru(ir,fpr2pos,5)<<1); |
978 | else |
979 | r2 = ((extru(ir,fpr2pos,5)<<1)|(extru(ir,fpxr2pos,1))); |
980 | fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; |
981 | if (r2 == 0) |
982 | r2 = fpzeroreg; |
983 | if (fpu_type_flags & PA2_0_FPU_FLAG) { |
984 | /* FTEST if nullify bit set, otherwise FCMP */ |
985 | if (extru(ir, fpnulpos, 1)) { /* FTEST */ |
986 | /* not legal */ |
987 | return(MAJOR_0E_EXCP); |
988 | } else { /* FCMP */ |
989 | switch (fmt) { |
990 | /* |
991 | * fmt is only 1 bit long |
992 | */ |
993 | case 0: |
994 | retval = sgl_fcmp(&fpregs[r1], |
995 | &fpregs[r2],extru(ir,fptpos,5), |
996 | &local_status); |
997 | update_status_cbit(status,local_status, |
998 | fpu_type_flags, subop); |
999 | return(retval); |
1000 | case 1: |
1001 | retval = dbl_fcmp(&fpregs[r1], |
1002 | &fpregs[r2],extru(ir,fptpos,5), |
1003 | &local_status); |
1004 | update_status_cbit(status,local_status, |
1005 | fpu_type_flags, subop); |
1006 | return(retval); |
1007 | } |
1008 | } |
1009 | } /* end of if for PA2.0 */ |
1010 | else { /* PA1.0 & PA1.1 */ |
1011 | switch (subop) { |
1012 | case 1: |
1013 | case 2: |
1014 | case 3: |
1015 | case 4: |
1016 | case 5: |
1017 | case 6: |
1018 | case 7: |
1019 | return(MAJOR_0E_EXCP); |
1020 | case 0: /* FCMP */ |
1021 | switch (fmt) { |
1022 | /* |
1023 | * fmt is only 1 bit long |
1024 | */ |
1025 | case 0: |
1026 | retval = sgl_fcmp(&fpregs[r1], |
1027 | &fpregs[r2],extru(ir,fptpos,5), |
1028 | &local_status); |
1029 | update_status_cbit(status,local_status, |
1030 | fpu_type_flags, subop); |
1031 | return(retval); |
1032 | case 1: |
1033 | retval = dbl_fcmp(&fpregs[r1], |
1034 | &fpregs[r2],extru(ir,fptpos,5), |
1035 | &local_status); |
1036 | update_status_cbit(status,local_status, |
1037 | fpu_type_flags, subop); |
1038 | return(retval); |
1039 | } |
1040 | } /* end of switch subop */ |
1041 | } /* end of else for PA1.0 & PA1.1 */ |
1042 | BUG(); |
1043 | case 3: /* class 3 */ |
1044 | /* |
1045 | * Be careful out there. |
1046 | * Crashme can generate cases where FR31R is specified |
1047 | * as the source or target of a double precision operation. |
1048 | * Since we just pass the address of the floating-point |
1049 | * register to the emulation routines, this can cause |
1050 | * corruption of fpzeroreg. |
1051 | */ |
1052 | if (fmt == DBL) |
1053 | r2 = (extru(ir,fpr2pos,5)<<1); |
1054 | else |
1055 | r2 = ((extru(ir,fpr2pos,5)<<1)|(extru(ir,fpxr2pos,1))); |
1056 | if (r2 == 0) |
1057 | r2 = fpzeroreg; |
1058 | switch (subop) { |
1059 | case 5: |
1060 | case 6: |
1061 | case 7: |
1062 | return(MAJOR_0E_EXCP); |
1063 | |
1064 | /* |
1065 | * Note that fmt is only 1 bit for class 3 */ |
1066 | case 0: /* FADD */ |
1067 | switch (fmt) { |
1068 | case 0: |
1069 | return(sgl_fadd(&fpregs[r1],&fpregs[r2], |
1070 | &fpregs[t],status)); |
1071 | case 1: |
1072 | return(dbl_fadd(&fpregs[r1],&fpregs[r2], |
1073 | &fpregs[t],status)); |
1074 | } |
1075 | BUG(); |
1076 | case 1: /* FSUB */ |
1077 | switch (fmt) { |
1078 | case 0: |
1079 | return(sgl_fsub(&fpregs[r1],&fpregs[r2], |
1080 | &fpregs[t],status)); |
1081 | case 1: |
1082 | return(dbl_fsub(&fpregs[r1],&fpregs[r2], |
1083 | &fpregs[t],status)); |
1084 | } |
1085 | BUG(); |
1086 | case 2: /* FMPY or XMPYU */ |
1087 | /* |
1088 | * check for integer multiply (x bit set) |
1089 | */ |
1090 | if (extru(ir,fpxpos,1)) { |
1091 | /* |
1092 | * emulate XMPYU |
1093 | */ |
1094 | switch (fmt) { |
1095 | case 0: |
1096 | /* |
1097 | * bad instruction if t specifies |
1098 | * the right half of a register |
1099 | */ |
1100 | if (t & 1) |
1101 | return(MAJOR_0E_EXCP); |
1102 | BUG(); |
1103 | /* unsupported |
1104 | * impyu(&fpregs[r1],&fpregs[r2], |
1105 | * &fpregs[t]); |
1106 | */ |
1107 | return(NOEXCEPTION); |
1108 | case 1: |
1109 | return(MAJOR_0E_EXCP); |
1110 | } |
1111 | } |
1112 | else { /* FMPY */ |
1113 | switch (fmt) { |
1114 | case 0: |
1115 | return(sgl_fmpy(&fpregs[r1], |
1116 | &fpregs[r2],&fpregs[t],status)); |
1117 | case 1: |
1118 | return(dbl_fmpy(&fpregs[r1], |
1119 | &fpregs[r2],&fpregs[t],status)); |
1120 | } |
1121 | } |
1122 | BUG(); |
1123 | case 3: /* FDIV */ |
1124 | switch (fmt) { |
1125 | case 0: |
1126 | return(sgl_fdiv(&fpregs[r1],&fpregs[r2], |
1127 | &fpregs[t],status)); |
1128 | case 1: |
1129 | return(dbl_fdiv(&fpregs[r1],&fpregs[r2], |
1130 | &fpregs[t],status)); |
1131 | } |
1132 | BUG(); |
1133 | case 4: /* FREM */ |
1134 | switch (fmt) { |
1135 | case 0: |
1136 | return(sgl_frem(&fpregs[r1],&fpregs[r2], |
1137 | &fpregs[t],status)); |
1138 | case 1: |
1139 | return(dbl_frem(&fpregs[r1],&fpregs[r2], |
1140 | &fpregs[t],status)); |
1141 | } |
1142 | } /* end of class 3 switch */ |
1143 | } /* end of switch(class) */ |
1144 | |
1145 | /* If we get here, something is really wrong! */ |
1146 | return(MAJOR_0E_EXCP); |
1147 | } |
1148 | |
1149 | |
1150 | /* |
1151 | * routine to decode the 06 (FMPYADD and FMPYCFXT) instruction |
1152 | */ |
1153 | static u_int |
1154 | decode_06(ir,fpregs) |
1155 | u_int ir; |
1156 | u_int fpregs[]; |
1157 | { |
1158 | u_int rm1, rm2, tm, ra, ta; /* operands */ |
1159 | u_int fmt; |
1160 | u_int error = 0; |
1161 | u_int status; |
1162 | u_int fpu_type_flags; |
1163 | union { |
1164 | double dbl; |
1165 | float flt; |
1166 | struct { u_int i1; u_int i2; } ints; |
1167 | } mtmp, atmp; |
1168 | |
1169 | |
1170 | status = fpregs[0]; /* use a local copy of status reg */ |
1171 | fpu_type_flags=fpregs[FPU_TYPE_FLAG_POS]; /* get fpu type flags */ |
1172 | fmt = extru(ir, fpmultifmt, 1); /* get sgl/dbl flag */ |
1173 | if (fmt == 0) { /* DBL */ |
1174 | rm1 = extru(ir, fprm1pos, 5) * sizeof(double)/sizeof(u_int); |
1175 | if (rm1 == 0) |
1176 | rm1 = fpzeroreg; |
1177 | rm2 = extru(ir, fprm2pos, 5) * sizeof(double)/sizeof(u_int); |
1178 | if (rm2 == 0) |
1179 | rm2 = fpzeroreg; |
1180 | tm = extru(ir, fptmpos, 5) * sizeof(double)/sizeof(u_int); |
1181 | if (tm == 0) |
1182 | return(MAJOR_06_EXCP); |
1183 | ra = extru(ir, fprapos, 5) * sizeof(double)/sizeof(u_int); |
1184 | ta = extru(ir, fptapos, 5) * sizeof(double)/sizeof(u_int); |
1185 | if (ta == 0) |
1186 | return(MAJOR_06_EXCP); |
1187 | |
1188 | if (fpu_type_flags & TIMEX_ROLEX_FPU_MASK) { |
1189 | |
1190 | if (ra == 0) { |
1191 | /* special case FMPYCFXT, see sgl case below */ |
1192 | if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2], |
1193 | &mtmp.ints.i1,&status)) |
1194 | error = 1; |
1195 | if (dbl_to_sgl_fcnvfxt(&fpregs[ta], |
1196 | &atmp.ints.i1,&atmp.ints.i1,&status)) |
1197 | error = 1; |
1198 | } |
1199 | else { |
1200 | |
1201 | if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, |
1202 | &status)) |
1203 | error = 1; |
1204 | if (dbl_fadd(&fpregs[ta], &fpregs[ra], &atmp.ints.i1, |
1205 | &status)) |
1206 | error = 1; |
1207 | } |
1208 | } |
1209 | |
1210 | else |
1211 | |
1212 | { |
1213 | if (ra == 0) |
1214 | ra = fpzeroreg; |
1215 | |
1216 | if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, |
1217 | &status)) |
1218 | error = 1; |
1219 | if (dbl_fadd(&fpregs[ta], &fpregs[ra], &atmp.ints.i1, |
1220 | &status)) |
1221 | error = 1; |
1222 | |
1223 | } |
1224 | |
1225 | if (error) |
1226 | return(MAJOR_06_EXCP); |
1227 | else { |
1228 | /* copy results */ |
1229 | fpregs[tm] = mtmp.ints.i1; |
1230 | fpregs[tm+1] = mtmp.ints.i2; |
1231 | fpregs[ta] = atmp.ints.i1; |
1232 | fpregs[ta+1] = atmp.ints.i2; |
1233 | fpregs[0] = status; |
1234 | return(NOEXCEPTION); |
1235 | } |
1236 | } |
1237 | else { /* SGL */ |
1238 | /* |
1239 | * calculate offsets for single precision numbers |
1240 | * See table 6-14 in PA-89 architecture for mapping |
1241 | */ |
1242 | rm1 = (extru(ir,fprm1pos,4) | 0x10 ) << 1; /* get offset */ |
1243 | rm1 |= extru(ir,fprm1pos-4,1); /* add right word offset */ |
1244 | |
1245 | rm2 = (extru(ir,fprm2pos,4) | 0x10 ) << 1; /* get offset */ |
1246 | rm2 |= extru(ir,fprm2pos-4,1); /* add right word offset */ |
1247 | |
1248 | tm = (extru(ir,fptmpos,4) | 0x10 ) << 1; /* get offset */ |
1249 | tm |= extru(ir,fptmpos-4,1); /* add right word offset */ |
1250 | |
1251 | ra = (extru(ir,fprapos,4) | 0x10 ) << 1; /* get offset */ |
1252 | ra |= extru(ir,fprapos-4,1); /* add right word offset */ |
1253 | |
1254 | ta = (extru(ir,fptapos,4) | 0x10 ) << 1; /* get offset */ |
1255 | ta |= extru(ir,fptapos-4,1); /* add right word offset */ |
1256 | |
1257 | if (ra == 0x20 &&(fpu_type_flags & TIMEX_ROLEX_FPU_MASK)) { |
1258 | /* special case FMPYCFXT (really 0) |
1259 | * This instruction is only present on the Timex and |
1260 | * Rolex fpu's in so if it is the special case and |
1261 | * one of these fpu's we run the FMPYCFXT instruction |
1262 | */ |
1263 | if (sgl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, |
1264 | &status)) |
1265 | error = 1; |
1266 | if (sgl_to_sgl_fcnvfxt(&fpregs[ta],&atmp.ints.i1, |
1267 | &atmp.ints.i1,&status)) |
1268 | error = 1; |
1269 | } |
1270 | else { |
1271 | if (sgl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1, |
1272 | &status)) |
1273 | error = 1; |
1274 | if (sgl_fadd(&fpregs[ta], &fpregs[ra], &atmp.ints.i1, |
1275 | &status)) |
1276 | error = 1; |
1277 | } |
1278 | if (error) |
1279 | return(MAJOR_06_EXCP); |
1280 | else { |
1281 | /* copy results */ |
1282 | fpregs[tm] = mtmp.ints.i1; |
1283 | fpregs[ta] = atmp.ints.i1; |
1284 | fpregs[0] = status; |
1285 | return(NOEXCEPTION); |
1286 | } |
1287 | } |
1288 | } |
1289 | |
1290 | /* |
1291 | * routine to decode the 26 (FMPYSUB) instruction |
1292 | */ |
1293 | static u_int |
1294 | decode_26(ir,fpregs) |
1295 | u_int ir; |
1296 | u_int fpregs[]; |
1297 | { |
1298 | u_int rm1, rm2, tm, ra, ta; /* operands */ |
1299 | u_int fmt; |
1300 | u_int error = 0; |
1301 | u_int status; |
1302 | union { |
1303 | double dbl; |
1304 | float flt; |
1305 | struct { u_int i1; u_int i2; } ints; |
1306 | } mtmp, atmp; |
1307 | |
1308 | |
1309 | status = fpregs[0]; |
1310 | fmt = extru(ir, fpmultifmt, 1); /* get sgl/dbl flag */ |
1311 | if (fmt == 0) { /* DBL */ |
1312 | rm1 = extru(ir, fprm1pos, 5) * sizeof(double)/sizeof(u_int); |
1313 | if (rm1 == 0) |
1314 | rm1 = fpzeroreg; |
1315 | rm2 = extru(ir, fprm2pos, 5) * sizeof(double)/sizeof(u_int); |
1316 | if (rm2 == 0) |
1317 | rm2 = fpzeroreg; |
1318 | tm = extru(ir, fptmpos, 5) * sizeof(double)/sizeof(u_int); |
1319 | if (tm == 0) |
1320 | return(MAJOR_26_EXCP); |
1321 | ra = extru(ir, fprapos, 5) * sizeof(double)/sizeof(u_int); |
1322 | if (ra == 0) |
1323 | return(MAJOR_26_EXCP); |
1324 | ta = extru(ir, fptapos, 5) * sizeof(double)/sizeof(u_int); |
1325 | if (ta == 0) |
1326 | return(MAJOR_26_EXCP); |
1327 | |
1328 | if (dbl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1,&status)) |
1329 | error = 1; |
1330 | if (dbl_fsub(&fpregs[ta], &fpregs[ra], &atmp.ints.i1,&status)) |
1331 | error = 1; |
1332 | if (error) |
1333 | return(MAJOR_26_EXCP); |
1334 | else { |
1335 | /* copy results */ |
1336 | fpregs[tm] = mtmp.ints.i1; |
1337 | fpregs[tm+1] = mtmp.ints.i2; |
1338 | fpregs[ta] = atmp.ints.i1; |
1339 | fpregs[ta+1] = atmp.ints.i2; |
1340 | fpregs[0] = status; |
1341 | return(NOEXCEPTION); |
1342 | } |
1343 | } |
1344 | else { /* SGL */ |
1345 | /* |
1346 | * calculate offsets for single precision numbers |
1347 | * See table 6-14 in PA-89 architecture for mapping |
1348 | */ |
1349 | rm1 = (extru(ir,fprm1pos,4) | 0x10 ) << 1; /* get offset */ |
1350 | rm1 |= extru(ir,fprm1pos-4,1); /* add right word offset */ |
1351 | |
1352 | rm2 = (extru(ir,fprm2pos,4) | 0x10 ) << 1; /* get offset */ |
1353 | rm2 |= extru(ir,fprm2pos-4,1); /* add right word offset */ |
1354 | |
1355 | tm = (extru(ir,fptmpos,4) | 0x10 ) << 1; /* get offset */ |
1356 | tm |= extru(ir,fptmpos-4,1); /* add right word offset */ |
1357 | |
1358 | ra = (extru(ir,fprapos,4) | 0x10 ) << 1; /* get offset */ |
1359 | ra |= extru(ir,fprapos-4,1); /* add right word offset */ |
1360 | |
1361 | ta = (extru(ir,fptapos,4) | 0x10 ) << 1; /* get offset */ |
1362 | ta |= extru(ir,fptapos-4,1); /* add right word offset */ |
1363 | |
1364 | if (sgl_fmpy(&fpregs[rm1],&fpregs[rm2],&mtmp.ints.i1,&status)) |
1365 | error = 1; |
1366 | if (sgl_fsub(&fpregs[ta], &fpregs[ra], &atmp.ints.i1,&status)) |
1367 | error = 1; |
1368 | if (error) |
1369 | return(MAJOR_26_EXCP); |
1370 | else { |
1371 | /* copy results */ |
1372 | fpregs[tm] = mtmp.ints.i1; |
1373 | fpregs[ta] = atmp.ints.i1; |
1374 | fpregs[0] = status; |
1375 | return(NOEXCEPTION); |
1376 | } |
1377 | } |
1378 | |
1379 | } |
1380 | |
1381 | /* |
1382 | * routine to decode the 2E (FMPYFADD,FMPYNFADD) instructions |
1383 | */ |
1384 | static u_int |
1385 | decode_2e(ir,fpregs) |
1386 | u_int ir; |
1387 | u_int fpregs[]; |
1388 | { |
1389 | u_int rm1, rm2, ra, t; /* operands */ |
1390 | u_int fmt; |
1391 | |
1392 | fmt = extru(ir,fpfmtpos,1); /* get fmt completer */ |
1393 | if (fmt == DBL) { /* DBL */ |
1394 | rm1 = extru(ir,fprm1pos,5) * sizeof(double)/sizeof(u_int); |
1395 | if (rm1 == 0) |
1396 | rm1 = fpzeroreg; |
1397 | rm2 = extru(ir,fprm2pos,5) * sizeof(double)/sizeof(u_int); |
1398 | if (rm2 == 0) |
1399 | rm2 = fpzeroreg; |
1400 | ra = ((extru(ir,fpraupos,3)<<2)|(extru(ir,fpralpos,3)>>1)) * |
1401 | sizeof(double)/sizeof(u_int); |
1402 | if (ra == 0) |
1403 | ra = fpzeroreg; |
1404 | t = extru(ir,fptpos,5) * sizeof(double)/sizeof(u_int); |
1405 | if (t == 0) |
1406 | return(MAJOR_2E_EXCP); |
1407 | |
1408 | if (extru(ir,fpfusedsubop,1)) { /* fmpyfadd or fmpynfadd? */ |
1409 | return(dbl_fmpynfadd(&fpregs[rm1], &fpregs[rm2], |
1410 | &fpregs[ra], &fpregs[0], &fpregs[t])); |
1411 | } else { |
1412 | return(dbl_fmpyfadd(&fpregs[rm1], &fpregs[rm2], |
1413 | &fpregs[ra], &fpregs[0], &fpregs[t])); |
1414 | } |
1415 | } /* end DBL */ |
1416 | else { /* SGL */ |
1417 | rm1 = (extru(ir,fprm1pos,5)<<1)|(extru(ir,fpxrm1pos,1)); |
1418 | if (rm1 == 0) |
1419 | rm1 = fpzeroreg; |
1420 | rm2 = (extru(ir,fprm2pos,5)<<1)|(extru(ir,fpxrm2pos,1)); |
1421 | if (rm2 == 0) |
1422 | rm2 = fpzeroreg; |
1423 | ra = (extru(ir,fpraupos,3)<<3)|extru(ir,fpralpos,3); |
1424 | if (ra == 0) |
1425 | ra = fpzeroreg; |
1426 | t = ((extru(ir,fptpos,5)<<1)|(extru(ir,fpxtpos,1))); |
1427 | if (t == 0) |
1428 | return(MAJOR_2E_EXCP); |
1429 | |
1430 | if (extru(ir,fpfusedsubop,1)) { /* fmpyfadd or fmpynfadd? */ |
1431 | return(sgl_fmpynfadd(&fpregs[rm1], &fpregs[rm2], |
1432 | &fpregs[ra], &fpregs[0], &fpregs[t])); |
1433 | } else { |
1434 | return(sgl_fmpyfadd(&fpregs[rm1], &fpregs[rm2], |
1435 | &fpregs[ra], &fpregs[0], &fpregs[t])); |
1436 | } |
1437 | } /* end SGL */ |
1438 | } |
1439 | |
1440 | /* |
1441 | * update_status_cbit |
1442 | * |
1443 | * This routine returns the correct FP status register value in |
1444 | * *status, based on the C-bit & V-bit returned by the FCMP |
1445 | * emulation routine in new_status. The architecture type |
1446 | * (PA83, PA89 or PA2.0) is available in fpu_type. The y_field |
1447 | * and the architecture type are used to determine what flavor |
1448 | * of FCMP is being emulated. |
1449 | */ |
1450 | static void |
1451 | update_status_cbit(status, new_status, fpu_type, y_field) |
1452 | u_int *status, new_status; |
1453 | u_int fpu_type; |
1454 | u_int y_field; |
1455 | { |
1456 | /* |
1457 | * For PA89 FPU's which implement the Compare Queue and |
1458 | * for PA2.0 FPU's, update the Compare Queue if the y-field = 0, |
1459 | * otherwise update the specified bit in the Compare Array. |
1460 | * Note that the y-field will always be 0 for non-PA2.0 FPU's. |
1461 | */ |
1462 | if ((fpu_type & TIMEX_EXTEN_FLAG) || |
1463 | (fpu_type & ROLEX_EXTEN_FLAG) || |
1464 | (fpu_type & PA2_0_FPU_FLAG)) { |
1465 | if (y_field == 0) { |
1466 | *status = ((*status & 0x04000000) >> 5) | /* old Cbit */ |
1467 | ((*status & 0x003ff000) >> 1) | /* old CQ */ |
1468 | (new_status & 0xffc007ff); /* all other bits*/ |
1469 | } else { |
1470 | *status = (*status & 0x04000000) | /* old Cbit */ |
1471 | ((new_status & 0x04000000) >> (y_field+4)) | |
1472 | (new_status & ~0x04000000 & /* other bits */ |
1473 | ~(0x04000000 >> (y_field+4))); |
1474 | } |
1475 | } |
1476 | /* if PA83, just update the C-bit */ |
1477 | else { |
1478 | *status = new_status; |
1479 | } |
1480 | } |
1481 | |