1 |
/* |
2 |
* compiler/compemu_fpp.cpp - Dynamic translation of FPU instructions |
3 |
* |
4 |
* Original 68040 JIT compiler for UAE, copyright 2000-2002 Bernd Meyer |
5 |
* |
6 |
* Adaptation for Basilisk II and improvements, copyright 2000-2005 |
7 |
* Gwenole Beauchesne |
8 |
* |
9 |
* Basilisk II (C) 1997-2008 Christian Bauer |
10 |
* |
11 |
* This program is free software; you can redistribute it and/or modify |
12 |
* it under the terms of the GNU General Public License as published by |
13 |
* the Free Software Foundation; either version 2 of the License, or |
14 |
* (at your option) any later version. |
15 |
* |
16 |
* This program is distributed in the hope that it will be useful, |
17 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 |
* GNU General Public License for more details. |
20 |
* |
21 |
* You should have received a copy of the GNU General Public License |
22 |
* along with this program; if not, write to the Free Software |
23 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 |
*/ |
25 |
|
26 |
/* |
27 |
* UAE - The Un*x Amiga Emulator |
28 |
* |
29 |
* MC68881 emulation |
30 |
* |
31 |
* Copyright 1996 Herman ten Brugge |
32 |
* Adapted for JIT compilation (c) Bernd Meyer, 2000 |
33 |
*/ |
34 |
|
35 |
#include "sysdeps.h" |
36 |
|
37 |
#include <math.h> |
38 |
#include <stdio.h> |
39 |
|
40 |
#include "memory.h" |
41 |
#include "readcpu.h" |
42 |
#include "newcpu.h" |
43 |
#include "main.h" |
44 |
#include "compiler/compemu.h" |
45 |
#include "fpu/fpu.h" |
46 |
#include "fpu/flags.h" |
47 |
#include "fpu/exceptions.h" |
48 |
#include "fpu/rounding.h" |
49 |
|
50 |
#define DEBUG 0 |
51 |
#include "debug.h" |
52 |
|
53 |
// gb-- WARNING: get_fpcr() and set_fpcr() support is experimental |
54 |
#define HANDLE_FPCR 0 |
55 |
|
56 |
// - IEEE-based fpu core must be used |
57 |
#if defined(FPU_IEEE) |
58 |
# define CAN_HANDLE_FPCR |
59 |
#endif |
60 |
|
61 |
// - Generic rounding mode and precision modes are supported if set together |
62 |
#if defined(FPU_USE_GENERIC_ROUNDING_MODE) && defined(FPU_USE_GENERIC_ROUNDING_PRECISION) |
63 |
# define CAN_HANDLE_FPCR |
64 |
#endif |
65 |
|
66 |
// - X86 rounding mode and precision modes are *not* supported but might work (?!) |
67 |
#if defined(FPU_USE_X86_ROUNDING_MODE) && defined(FPU_USE_X86_ROUNDING_PRECISION) |
68 |
# define CAN_HANDLE_FPCR |
69 |
#endif |
70 |
|
71 |
#if HANDLE_FPCR && !defined(CAN_HANDLE_FPCR) |
72 |
# warning "Can't handle FPCR, will FAIL(1) at runtime" |
73 |
# undef HANDLE_FPCR |
74 |
# define HANDLE_FPCR 0 |
75 |
#endif |
76 |
|
77 |
#define STATIC_INLINE static inline |
78 |
#define MAKE_FPSR(r) do { fmov_rr(FP_RESULT,r); } while (0) |
79 |
|
80 |
#define delay nop() ;nop() |
81 |
#define delay2 nop() ;nop() |
82 |
|
83 |
#define UNKNOWN_EXTRA 0xFFFFFFFF |
84 |
static void fpuop_illg(uae_u32 opcode, uae_u32 extra) |
85 |
{ |
86 |
/* |
87 |
if (extra == UNKNOWN_EXTRA) |
88 |
printf("FPU opcode %x, extra UNKNOWN_EXTRA\n",opcode & 0xFFFF); |
89 |
else |
90 |
printf("FPU opcode %x, extra %x\n",opcode & 0xFFFF,extra & 0xFFFF); |
91 |
*/ |
92 |
op_illg(opcode); |
93 |
} |
94 |
|
95 |
static uae_s32 temp_fp[4]; /* To convert between FP/integer */ |
96 |
|
97 |
/* return register number, or -1 for failure */ |
98 |
STATIC_INLINE int get_fp_value (uae_u32 opcode, uae_u16 extra) |
99 |
{ |
100 |
uaecptr tmppc; |
101 |
uae_u16 tmp; |
102 |
int size; |
103 |
int mode; |
104 |
int reg; |
105 |
double* src; |
106 |
uae_u32 ad = 0; |
107 |
static int sz1[8] = { 4, 4, 12, 12, 2, 8, 1, 0 }; |
108 |
static int sz2[8] = { 4, 4, 12, 12, 2, 8, 2, 0 }; |
109 |
|
110 |
if ((extra & 0x4000) == 0) { |
111 |
return ((extra >> 10) & 7); |
112 |
} |
113 |
|
114 |
mode = (opcode >> 3) & 7; |
115 |
reg = opcode & 7; |
116 |
size = (extra >> 10) & 7; |
117 |
switch (mode) { |
118 |
case 0: |
119 |
switch (size) { |
120 |
case 6: |
121 |
sign_extend_8_rr(S1,reg); |
122 |
mov_l_mr((uintptr)temp_fp,S1); |
123 |
delay2; |
124 |
fmovi_rm(FS1,(uintptr)temp_fp); |
125 |
return FS1; |
126 |
case 4: |
127 |
sign_extend_16_rr(S1,reg); |
128 |
mov_l_mr((uintptr)temp_fp,S1); |
129 |
delay2; |
130 |
fmovi_rm(FS1,(uintptr)temp_fp); |
131 |
return FS1; |
132 |
case 0: |
133 |
mov_l_mr((uintptr)temp_fp,reg); |
134 |
delay2; |
135 |
fmovi_rm(FS1,(uintptr)temp_fp); |
136 |
return FS1; |
137 |
case 1: |
138 |
mov_l_mr((uintptr)temp_fp,reg); |
139 |
delay2; |
140 |
fmovs_rm(FS1,(uintptr)temp_fp); |
141 |
return FS1; |
142 |
default: |
143 |
return -1; |
144 |
} |
145 |
return -1; /* Should be unreachable */ |
146 |
case 1: |
147 |
return -1; /* Genuine invalid instruction */ |
148 |
default: |
149 |
break; |
150 |
} |
151 |
/* OK, we *will* have to load something from an address. Let's make |
152 |
sure we know how to handle that, or quit early --- i.e. *before* |
153 |
we do any postincrement/predecrement that we may regret */ |
154 |
|
155 |
switch (size) { |
156 |
case 3: |
157 |
return -1; |
158 |
case 0: |
159 |
case 1: |
160 |
case 2: |
161 |
case 4: |
162 |
case 5: |
163 |
case 6: |
164 |
break; |
165 |
default: |
166 |
return -1; |
167 |
} |
168 |
|
169 |
switch (mode) { |
170 |
case 2: |
171 |
ad=S1; /* We will change it, anyway ;-) */ |
172 |
mov_l_rr(ad,reg+8); |
173 |
break; |
174 |
case 3: |
175 |
ad=S1; |
176 |
mov_l_rr(ad,reg+8); |
177 |
lea_l_brr(reg+8,reg+8,(reg == 7?sz2[size]:sz1[size])); |
178 |
break; |
179 |
case 4: |
180 |
ad=S1; |
181 |
|
182 |
lea_l_brr(reg+8,reg+8,-(reg == 7?sz2[size]:sz1[size])); |
183 |
mov_l_rr(ad,reg+8); |
184 |
break; |
185 |
case 5: |
186 |
{ |
187 |
uae_u32 off=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
188 |
ad=S1; |
189 |
mov_l_rr(ad,reg+8); |
190 |
lea_l_brr(ad,ad,off); |
191 |
break; |
192 |
} |
193 |
case 6: |
194 |
{ |
195 |
uae_u32 dp=comp_get_iword((m68k_pc_offset+=2)-2); |
196 |
ad=S1; |
197 |
calc_disp_ea_020(reg+8,dp,ad,S2); |
198 |
break; |
199 |
} |
200 |
case 7: |
201 |
switch (reg) { |
202 |
case 0: |
203 |
{ |
204 |
uae_u32 off=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
205 |
ad=S1; |
206 |
mov_l_ri(ad,off); |
207 |
break; |
208 |
} |
209 |
case 1: |
210 |
{ |
211 |
uae_u32 off=comp_get_ilong((m68k_pc_offset+=4)-4); |
212 |
ad=S1; |
213 |
mov_l_ri(ad,off); |
214 |
break; |
215 |
} |
216 |
case 2: |
217 |
{ |
218 |
uae_u32 address=start_pc+((char *)comp_pc_p-(char *)start_pc_p)+ |
219 |
m68k_pc_offset; |
220 |
uae_s32 PC16off =(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2) |
221 |
-2); |
222 |
ad=S1; |
223 |
mov_l_ri(ad,address+PC16off); |
224 |
break; |
225 |
} |
226 |
case 3: |
227 |
return -1; |
228 |
tmppc = m68k_getpc (); |
229 |
tmp = next_iword (); |
230 |
ad = get_disp_ea_020 (tmppc, tmp); |
231 |
break; |
232 |
case 4: |
233 |
{ |
234 |
uae_u32 address=start_pc+((char *)comp_pc_p-(char *)start_pc_p)+ m68k_pc_offset; |
235 |
ad=S1; |
236 |
// Immediate addressing mode && Operation Length == Byte -> |
237 |
// Use the low-order byte of the extension word. |
238 |
if (size == 6) address++; |
239 |
mov_l_ri(ad,address); |
240 |
m68k_pc_offset+=sz2[size]; |
241 |
break; |
242 |
} |
243 |
default: |
244 |
return -1; |
245 |
} |
246 |
} |
247 |
|
248 |
switch (size) { |
249 |
case 0: |
250 |
readlong(ad,S2,S3); |
251 |
mov_l_mr((uintptr)temp_fp,S2); |
252 |
delay2; |
253 |
fmovi_rm(FS1,(uintptr)temp_fp); |
254 |
break; |
255 |
case 1: |
256 |
readlong(ad,S2,S3); |
257 |
mov_l_mr((uintptr)temp_fp,S2); |
258 |
delay2; |
259 |
fmovs_rm(FS1,(uintptr)temp_fp); |
260 |
break; |
261 |
case 2: |
262 |
readword(ad,S2,S3); |
263 |
mov_w_mr(((uintptr)temp_fp)+8,S2); |
264 |
add_l_ri(ad,4); |
265 |
readlong(ad,S2,S3); |
266 |
mov_l_mr((uintptr)(temp_fp)+4,S2); |
267 |
add_l_ri(ad,4); |
268 |
readlong(ad,S2,S3); |
269 |
mov_l_mr((uintptr)(temp_fp),S2); |
270 |
delay2; |
271 |
fmov_ext_rm(FS1,(uintptr)(temp_fp)); |
272 |
break; |
273 |
case 3: |
274 |
return -1; /* Some silly "packed" stuff */ |
275 |
case 4: |
276 |
readword(ad,S2,S3); |
277 |
sign_extend_16_rr(S2,S2); |
278 |
mov_l_mr((uintptr)temp_fp,S2); |
279 |
delay2; |
280 |
fmovi_rm(FS1,(uintptr)temp_fp); |
281 |
break; |
282 |
case 5: |
283 |
readlong(ad,S2,S3); |
284 |
mov_l_mr(((uintptr)temp_fp)+4,S2); |
285 |
add_l_ri(ad,4); |
286 |
readlong(ad,S2,S3); |
287 |
mov_l_mr((uintptr)(temp_fp),S2); |
288 |
delay2; |
289 |
fmov_rm(FS1,(uintptr)(temp_fp)); |
290 |
break; |
291 |
case 6: |
292 |
readbyte(ad,S2,S3); |
293 |
sign_extend_8_rr(S2,S2); |
294 |
mov_l_mr((uintptr)temp_fp,S2); |
295 |
delay2; |
296 |
fmovi_rm(FS1,(uintptr)temp_fp); |
297 |
break; |
298 |
default: |
299 |
return -1; |
300 |
} |
301 |
return FS1; |
302 |
} |
303 |
|
304 |
/* return of -1 means failure, >=0 means OK */ |
305 |
STATIC_INLINE int put_fp_value (int val, uae_u32 opcode, uae_u16 extra) |
306 |
{ |
307 |
uae_u16 tmp; |
308 |
uaecptr tmppc; |
309 |
int size; |
310 |
int mode; |
311 |
int reg; |
312 |
uae_u32 ad; |
313 |
static int sz1[8] = { 4, 4, 12, 12, 2, 8, 1, 0 }; |
314 |
static int sz2[8] = { 4, 4, 12, 12, 2, 8, 2, 0 }; |
315 |
|
316 |
if ((extra & 0x4000) == 0) { |
317 |
const int dest_reg = (extra >> 10) & 7; |
318 |
fmov_rr(dest_reg, val); |
319 |
// gb-- status register is affected |
320 |
MAKE_FPSR(dest_reg); |
321 |
return 0; |
322 |
} |
323 |
|
324 |
mode = (opcode >> 3) & 7; |
325 |
reg = opcode & 7; |
326 |
size = (extra >> 10) & 7; |
327 |
ad = (uae_u32)-1; |
328 |
switch (mode) { |
329 |
case 0: |
330 |
switch (size) { |
331 |
case 6: |
332 |
fmovi_mr((uintptr)temp_fp,val); |
333 |
delay; |
334 |
mov_b_rm(reg,(uintptr)temp_fp); |
335 |
return 0; |
336 |
case 4: |
337 |
fmovi_mr((uintptr)temp_fp,val); |
338 |
delay; |
339 |
mov_w_rm(reg,(uintptr)temp_fp); |
340 |
return 0; |
341 |
case 0: |
342 |
fmovi_mr((uintptr)temp_fp,val); |
343 |
delay; |
344 |
mov_l_rm(reg,(uintptr)temp_fp); |
345 |
return 0; |
346 |
case 1: |
347 |
fmovs_mr((uintptr)temp_fp,val); |
348 |
delay; |
349 |
mov_l_rm(reg,(uintptr)temp_fp); |
350 |
return 0; |
351 |
default: |
352 |
return -1; |
353 |
} |
354 |
case 1: |
355 |
return -1; /* genuine invalid instruction */ |
356 |
default: break; |
357 |
} |
358 |
|
359 |
/* Let's make sure we get out *before* doing something silly if |
360 |
we can't handle the size */ |
361 |
switch (size) { |
362 |
case 0: |
363 |
case 4: |
364 |
case 5: |
365 |
case 6: |
366 |
case 2: |
367 |
case 1: |
368 |
break; |
369 |
case 3: |
370 |
default: |
371 |
return -1; |
372 |
} |
373 |
|
374 |
switch (mode) { |
375 |
case 2: |
376 |
ad=S1; |
377 |
mov_l_rr(ad,reg+8); |
378 |
break; |
379 |
case 3: |
380 |
ad=S1; |
381 |
mov_l_rr(ad,reg+8); |
382 |
lea_l_brr(reg+8,reg+8,(reg == 7?sz2[size]:sz1[size])); |
383 |
break; |
384 |
case 4: |
385 |
ad=S1; |
386 |
lea_l_brr(reg+8,reg+8,-(reg == 7?sz2[size]:sz1[size])); |
387 |
mov_l_rr(ad,reg+8); |
388 |
break; |
389 |
case 5: |
390 |
{ |
391 |
uae_u32 off=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
392 |
ad=S1; |
393 |
mov_l_rr(ad,reg+8); |
394 |
add_l_ri(ad,off); |
395 |
break; |
396 |
} |
397 |
case 6: |
398 |
{ |
399 |
uae_u32 dp=comp_get_iword((m68k_pc_offset+=2)-2); |
400 |
ad=S1; |
401 |
calc_disp_ea_020(reg+8,dp,ad,S2); |
402 |
break; |
403 |
} |
404 |
case 7: |
405 |
switch (reg) { |
406 |
case 0: |
407 |
{ |
408 |
uae_u32 off=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
409 |
ad=S1; |
410 |
mov_l_ri(ad,off); |
411 |
break; |
412 |
} |
413 |
case 1: |
414 |
{ |
415 |
uae_u32 off=comp_get_ilong((m68k_pc_offset+=4)-4); |
416 |
ad=S1; |
417 |
mov_l_ri(ad,off); |
418 |
break; |
419 |
} |
420 |
case 2: |
421 |
{ |
422 |
uae_u32 address=start_pc+((char *)comp_pc_p-(char *)start_pc_p)+ |
423 |
m68k_pc_offset; |
424 |
uae_s32 PC16off =(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
425 |
ad=S1; |
426 |
mov_l_ri(ad,address+PC16off); |
427 |
break; |
428 |
} |
429 |
case 3: |
430 |
return -1; |
431 |
tmppc = m68k_getpc (); |
432 |
tmp = next_iword (); |
433 |
ad = get_disp_ea_020 (tmppc, tmp); |
434 |
break; |
435 |
case 4: |
436 |
{ |
437 |
uae_u32 address=start_pc+((char *)comp_pc_p-(char *)start_pc_p)+ |
438 |
m68k_pc_offset; |
439 |
ad=S1; |
440 |
mov_l_ri(ad,address); |
441 |
m68k_pc_offset+=sz2[size]; |
442 |
break; |
443 |
} |
444 |
default: |
445 |
return -1; |
446 |
} |
447 |
} |
448 |
switch (size) { |
449 |
case 0: |
450 |
fmovi_mr((uintptr)temp_fp,val); |
451 |
delay; |
452 |
mov_l_rm(S2,(uintptr)temp_fp); |
453 |
writelong_clobber(ad,S2,S3); |
454 |
break; |
455 |
case 1: |
456 |
fmovs_mr((uintptr)temp_fp,val); |
457 |
delay; |
458 |
mov_l_rm(S2,(uintptr)temp_fp); |
459 |
writelong_clobber(ad,S2,S3); |
460 |
break; |
461 |
case 2: |
462 |
fmov_ext_mr((uintptr)temp_fp,val); |
463 |
delay; |
464 |
mov_w_rm(S2,(uintptr)temp_fp+8); |
465 |
writeword_clobber(ad,S2,S3); |
466 |
add_l_ri(ad,4); |
467 |
mov_l_rm(S2,(uintptr)temp_fp+4); |
468 |
writelong_clobber(ad,S2,S3); |
469 |
add_l_ri(ad,4); |
470 |
mov_l_rm(S2,(uintptr)temp_fp); |
471 |
writelong_clobber(ad,S2,S3); |
472 |
break; |
473 |
case 3: return -1; /* Packed */ |
474 |
|
475 |
case 4: |
476 |
fmovi_mr((uintptr)temp_fp,val); |
477 |
delay; |
478 |
mov_l_rm(S2,(uintptr)temp_fp); |
479 |
writeword_clobber(ad,S2,S3); |
480 |
break; |
481 |
case 5: |
482 |
fmov_mr((uintptr)temp_fp,val); |
483 |
delay; |
484 |
mov_l_rm(S2,(uintptr)temp_fp+4); |
485 |
writelong_clobber(ad,S2,S3); |
486 |
add_l_ri(ad,4); |
487 |
mov_l_rm(S2,(uintptr)temp_fp); |
488 |
writelong_clobber(ad,S2,S3); |
489 |
break; |
490 |
case 6: |
491 |
fmovi_mr((uintptr)temp_fp,val); |
492 |
delay; |
493 |
mov_l_rm(S2,(uintptr)temp_fp); |
494 |
writebyte(ad,S2,S3); |
495 |
break; |
496 |
default: |
497 |
return -1; |
498 |
} |
499 |
return 0; |
500 |
} |
501 |
|
502 |
/* return -1 for failure, or register number for success */ |
503 |
STATIC_INLINE int get_fp_ad (uae_u32 opcode, uae_u32 * ad) |
504 |
{ |
505 |
uae_u16 tmp; |
506 |
uaecptr tmppc; |
507 |
int mode; |
508 |
int reg; |
509 |
uae_s32 off; |
510 |
|
511 |
mode = (opcode >> 3) & 7; |
512 |
reg = opcode & 7; |
513 |
switch (mode) { |
514 |
case 0: |
515 |
case 1: |
516 |
return -1; |
517 |
case 2: |
518 |
case 3: |
519 |
case 4: |
520 |
mov_l_rr(S1,8+reg); |
521 |
return S1; |
522 |
*ad = m68k_areg (regs, reg); |
523 |
break; |
524 |
case 5: |
525 |
off=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
526 |
|
527 |
mov_l_rr(S1,8+reg); |
528 |
add_l_ri(S1,off); |
529 |
return S1; |
530 |
case 6: |
531 |
return -1; |
532 |
break; |
533 |
case 7: |
534 |
switch (reg) { |
535 |
case 0: |
536 |
off=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
537 |
mov_l_ri(S1,off); |
538 |
return S1; |
539 |
case 1: |
540 |
off=comp_get_ilong((m68k_pc_offset+=4)-4); |
541 |
mov_l_ri(S1,off); |
542 |
return S1; |
543 |
case 2: |
544 |
return -1; |
545 |
// *ad = m68k_getpc (); |
546 |
// *ad += (uae_s32) (uae_s16) next_iword (); |
547 |
off=start_pc+((char *)comp_pc_p-(char *)start_pc_p)+m68k_pc_offset; |
548 |
off+=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
549 |
mov_l_ri(S1,off); |
550 |
return S1; |
551 |
case 3: |
552 |
return -1; |
553 |
tmppc = m68k_getpc (); |
554 |
tmp = next_iword (); |
555 |
*ad = get_disp_ea_020 (tmppc, tmp); |
556 |
break; |
557 |
default: |
558 |
return -1; |
559 |
} |
560 |
} |
561 |
abort(); |
562 |
} |
563 |
|
564 |
void comp_fdbcc_opp (uae_u32 opcode, uae_u16 extra) |
565 |
{ |
566 |
FAIL(1); |
567 |
return; |
568 |
} |
569 |
|
570 |
void comp_fscc_opp (uae_u32 opcode, uae_u16 extra) |
571 |
{ |
572 |
uae_u32 ad; |
573 |
int cc; |
574 |
int reg; |
575 |
|
576 |
#if DEBUG_FPP |
577 |
printf ("fscc_opp at %08lx\n", m68k_getpc ()); |
578 |
fflush (stdout); |
579 |
#endif |
580 |
|
581 |
|
582 |
if (extra&0x20) { /* only cc from 00 to 1f are defined */ |
583 |
FAIL(1); |
584 |
return; |
585 |
} |
586 |
if ((opcode & 0x38) != 0) { /* We can only do to integer register */ |
587 |
FAIL(1); |
588 |
return; |
589 |
} |
590 |
|
591 |
fflags_into_flags(S2); |
592 |
reg=(opcode&7); |
593 |
|
594 |
mov_l_ri(S1,255); |
595 |
mov_l_ri(S4,0); |
596 |
switch(extra&0x0f) { /* according to fpp.c, the 0x10 bit is ignored |
597 |
*/ |
598 |
case 0: break; /* set never */ |
599 |
case 1: mov_l_rr(S2,S4); |
600 |
cmov_l_rr(S4,S1,4); |
601 |
cmov_l_rr(S4,S2,10); break; |
602 |
case 2: cmov_l_rr(S4,S1,7); break; |
603 |
case 3: cmov_l_rr(S4,S1,3); break; |
604 |
case 4: mov_l_rr(S2,S4); |
605 |
cmov_l_rr(S4,S1,2); |
606 |
cmov_l_rr(S4,S2,10); break; |
607 |
case 5: mov_l_rr(S2,S4); |
608 |
cmov_l_rr(S4,S1,6); |
609 |
cmov_l_rr(S4,S2,10); break; |
610 |
case 6: cmov_l_rr(S4,S1,5); break; |
611 |
case 7: cmov_l_rr(S4,S1,11); break; |
612 |
case 8: cmov_l_rr(S4,S1,10); break; |
613 |
case 9: cmov_l_rr(S4,S1,4); break; |
614 |
case 10: cmov_l_rr(S4,S1,10); cmov_l_rr(S4,S1,7); break; |
615 |
case 11: cmov_l_rr(S4,S1,4); cmov_l_rr(S4,S1,3); break; |
616 |
case 12: cmov_l_rr(S4,S1,2); break; |
617 |
case 13: cmov_l_rr(S4,S1,6); break; |
618 |
case 14: cmov_l_rr(S4,S1,5); cmov_l_rr(S4,S1,10); break; |
619 |
case 15: mov_l_rr(S4,S1); break; |
620 |
} |
621 |
|
622 |
if ((opcode & 0x38) == 0) { |
623 |
mov_b_rr(reg,S4); |
624 |
} else { |
625 |
abort(); |
626 |
if (get_fp_ad (opcode, &ad) == 0) { |
627 |
m68k_setpc (m68k_getpc () - 4); |
628 |
fpuop_illg (opcode,extra); |
629 |
} else |
630 |
put_byte (ad, cc ? 0xff : 0x00); |
631 |
} |
632 |
} |
633 |
|
634 |
void comp_ftrapcc_opp (uae_u32 opcode, uaecptr oldpc) |
635 |
{ |
636 |
int cc; |
637 |
|
638 |
FAIL(1); |
639 |
return; |
640 |
} |
641 |
|
642 |
void comp_fbcc_opp (uae_u32 opcode) |
643 |
{ |
644 |
uae_u32 start_68k_offset=m68k_pc_offset; |
645 |
uae_u32 off; |
646 |
uae_u32 v1; |
647 |
uae_u32 v2; |
648 |
uae_u32 nh; |
649 |
int cc; |
650 |
|
651 |
// comp_pc_p is expected to be bound to 32-bit addresses |
652 |
assert((uintptr)comp_pc_p <= 0xffffffffUL); |
653 |
|
654 |
if (opcode&0x20) { /* only cc from 00 to 1f are defined */ |
655 |
FAIL(1); |
656 |
return; |
657 |
} |
658 |
if ((opcode&0x40)==0) { |
659 |
off=(uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2); |
660 |
} |
661 |
else { |
662 |
off=comp_get_ilong((m68k_pc_offset+=4)-4); |
663 |
} |
664 |
mov_l_ri(S1,(uintptr) |
665 |
(comp_pc_p+off-(m68k_pc_offset-start_68k_offset))); |
666 |
mov_l_ri(PC_P,(uintptr)comp_pc_p); |
667 |
|
668 |
/* Now they are both constant. Might as well fold in m68k_pc_offset */ |
669 |
add_l_ri(S1,m68k_pc_offset); |
670 |
add_l_ri(PC_P,m68k_pc_offset); |
671 |
m68k_pc_offset=0; |
672 |
|
673 |
/* according to fpp.c, the 0x10 bit is ignored |
674 |
(it handles exception handling, which we don't |
675 |
do, anyway ;-) */ |
676 |
cc=opcode&0x0f; |
677 |
v1=get_const(PC_P); |
678 |
v2=get_const(S1); |
679 |
fflags_into_flags(S2); |
680 |
|
681 |
switch(cc) { |
682 |
case 0: break; /* jump never */ |
683 |
case 1: |
684 |
mov_l_rr(S2,PC_P); |
685 |
cmov_l_rr(PC_P,S1,4); |
686 |
cmov_l_rr(PC_P,S2,10); break; |
687 |
case 2: register_branch(v1,v2,7); break; |
688 |
case 3: register_branch(v1,v2,3); break; |
689 |
case 4: |
690 |
mov_l_rr(S2,PC_P); |
691 |
cmov_l_rr(PC_P,S1,2); |
692 |
cmov_l_rr(PC_P,S2,10); break; |
693 |
case 5: |
694 |
mov_l_rr(S2,PC_P); |
695 |
cmov_l_rr(PC_P,S1,6); |
696 |
cmov_l_rr(PC_P,S2,10); break; |
697 |
case 6: register_branch(v1,v2,5); break; |
698 |
case 7: register_branch(v1,v2,11); break; |
699 |
case 8: register_branch(v1,v2,10); break; |
700 |
case 9: register_branch(v1,v2,4); break; |
701 |
case 10: |
702 |
cmov_l_rr(PC_P,S1,10); |
703 |
cmov_l_rr(PC_P,S1,7); break; |
704 |
case 11: |
705 |
cmov_l_rr(PC_P,S1,4); |
706 |
cmov_l_rr(PC_P,S1,3); break; |
707 |
case 12: register_branch(v1,v2,2); break; |
708 |
case 13: register_branch(v1,v2,6); break; |
709 |
case 14: |
710 |
cmov_l_rr(PC_P,S1,5); |
711 |
cmov_l_rr(PC_P,S1,10); break; |
712 |
case 15: mov_l_rr(PC_P,S1); break; |
713 |
} |
714 |
} |
715 |
|
716 |
/* Floating point conditions |
717 |
The "NotANumber" part could be problematic; Howver, when NaN is |
718 |
encountered, the ftst instruction sets bot N and Z to 1 on the x87, |
719 |
so quite often things just fall into place. This is probably not |
720 |
accurate wrt the 68k FPU, but it is *as* accurate as this was before. |
721 |
However, some more thought should go into fixing this stuff up so |
722 |
it accurately emulates the 68k FPU. |
723 |
>=<U |
724 |
0000 0x00: 0 --- Never jump |
725 |
0101 0x01: Z --- jump if zero (x86: 4) |
726 |
1000 0x02: !(NotANumber || Z || N) --- Neither Z nor N set (x86: 7) |
727 |
1101 0x03: Z || !(NotANumber || N); --- Z or !N (x86: 4 and 3) |
728 |
0010 0x04: N && !(NotANumber || Z); --- N and !Z (x86: hard!) |
729 |
0111 0x05: Z || (N && !NotANumber); --- Z or N (x86: 6) |
730 |
1010 0x06: !(NotANumber || Z); --- not Z (x86: 5) |
731 |
1110 0x07: !NotANumber; --- not NaN (x86: 11, not parity) |
732 |
0001 0x08: NotANumber; --- NaN (x86: 10) |
733 |
0101 0x09: NotANumber || Z; --- Z (x86: 4) |
734 |
1001 0x0a: NotANumber || !(N || Z); --- NaN or neither N nor Z (x86: 10 and 7) |
735 |
1101 0x0b: NotANumber || Z || !N; --- Z or !N (x86: 4 and 3) |
736 |
0011 0x0c: NotANumber || (N && !Z); --- N (x86: 2) |
737 |
0111 0x0d: NotANumber || Z || N; --- Z or N (x86: 6) |
738 |
1010 0x0e: !Z; --- not Z (x86: 5) |
739 |
1111 0x0f: 1; --- always |
740 |
|
741 |
This is not how the 68k handles things, though --- it sets Z to 0 and N |
742 |
to the NaN's sign.... ('o' and 'i' denote differences from the above |
743 |
table) |
744 |
|
745 |
>=<U |
746 |
0000 0x00: 0 --- Never jump |
747 |
010o 0x01: Z --- jump if zero (x86: 4, not 10) |
748 |
1000 0x02: !(NotANumber || Z || N) --- Neither Z nor N set (x86: 7) |
749 |
110o 0x03: Z || !(NotANumber || N); --- Z or !N (x86: 3) |
750 |
0010 0x04: N && !(NotANumber || Z); --- N and !Z (x86: 2, not 10) |
751 |
011o 0x05: Z || (N && !NotANumber); --- Z or N (x86: 6, not 10) |
752 |
1010 0x06: !(NotANumber || Z); --- not Z (x86: 5) |
753 |
1110 0x07: !NotANumber; --- not NaN (x86: 11, not parity) |
754 |
0001 0x08: NotANumber; --- NaN (x86: 10) |
755 |
0101 0x09: NotANumber || Z; --- Z (x86: 4) |
756 |
1001 0x0a: NotANumber || !(N || Z); --- NaN or neither N nor Z (x86: 10 and 7) |
757 |
1101 0x0b: NotANumber || Z || !N; --- Z or !N (x86: 4 and 3) |
758 |
0011 0x0c: NotANumber || (N && !Z); --- N (x86: 2) |
759 |
0111 0x0d: NotANumber || Z || N; --- Z or N (x86: 6) |
760 |
101i 0x0e: !Z; --- not Z (x86: 5 and 10) |
761 |
1111 0x0f: 1; --- always |
762 |
|
763 |
Of course, this *still* doesn't mean that the x86 and 68k conditions are |
764 |
equivalent --- the handling of infinities is different, for one thing. |
765 |
On the 68k, +infinity minus +infinity is NotANumber (as it should be). On |
766 |
the x86, it is +infinity, and some exception is raised (which I suspect |
767 |
is promptly ignored) STUPID! |
768 |
The more I learn about their CPUs, the more I detest Intel.... |
769 |
|
770 |
You can see this in action if you have "Benoit" (see Aminet) and |
771 |
set the exponent to 16. Wait for a long time, and marvel at the extra black |
772 |
areas outside the center one. That's where Benoit expects NaN, and the x86 |
773 |
gives +infinity. [Ooops --- that must have been some kind of bug in my code. |
774 |
it no longer happens, and the resulting graphic looks much better, too] |
775 |
|
776 |
x86 conditions |
777 |
0011 : 2 |
778 |
1100 : 3 |
779 |
0101 : 4 |
780 |
1010 : 5 |
781 |
0111 : 6 |
782 |
1000 : 7 |
783 |
0001 : 10 |
784 |
1110 : 11 |
785 |
*/ |
786 |
void comp_fsave_opp (uae_u32 opcode) |
787 |
{ |
788 |
uae_u32 ad; |
789 |
int incr = (opcode & 0x38) == 0x20 ? -1 : 1; |
790 |
int i; |
791 |
|
792 |
FAIL(1); |
793 |
return; |
794 |
|
795 |
#if DEBUG_FPP |
796 |
printf ("fsave_opp at %08lx\n", m68k_getpc ()); |
797 |
fflush (stdout); |
798 |
#endif |
799 |
if (get_fp_ad (opcode, &ad) == 0) { |
800 |
m68k_setpc (m68k_getpc () - 2); |
801 |
fpuop_illg (opcode,UNKNOWN_EXTRA); |
802 |
return; |
803 |
} |
804 |
|
805 |
if (CPUType == 4) { |
806 |
/* 4 byte 68040 IDLE frame. */ |
807 |
if (incr < 0) { |
808 |
ad -= 4; |
809 |
put_long (ad, 0x41000000); |
810 |
} else { |
811 |
put_long (ad, 0x41000000); |
812 |
ad += 4; |
813 |
} |
814 |
} else { |
815 |
if (incr < 0) { |
816 |
ad -= 4; |
817 |
put_long (ad, 0x70000000); |
818 |
for (i = 0; i < 5; i++) { |
819 |
ad -= 4; |
820 |
put_long (ad, 0x00000000); |
821 |
} |
822 |
ad -= 4; |
823 |
put_long (ad, 0x1f180000); |
824 |
} else { |
825 |
put_long (ad, 0x1f180000); |
826 |
ad += 4; |
827 |
for (i = 0; i < 5; i++) { |
828 |
put_long (ad, 0x00000000); |
829 |
ad += 4; |
830 |
} |
831 |
put_long (ad, 0x70000000); |
832 |
ad += 4; |
833 |
} |
834 |
} |
835 |
if ((opcode & 0x38) == 0x18) |
836 |
m68k_areg (regs, opcode & 7) = ad; |
837 |
if ((opcode & 0x38) == 0x20) |
838 |
m68k_areg (regs, opcode & 7) = ad; |
839 |
} |
840 |
|
841 |
void comp_frestore_opp (uae_u32 opcode) |
842 |
{ |
843 |
uae_u32 ad; |
844 |
uae_u32 d; |
845 |
int incr = (opcode & 0x38) == 0x20 ? -1 : 1; |
846 |
|
847 |
FAIL(1); |
848 |
return; |
849 |
|
850 |
#if DEBUG_FPP |
851 |
printf ("frestore_opp at %08lx\n", m68k_getpc ()); |
852 |
fflush (stdout); |
853 |
#endif |
854 |
if (get_fp_ad (opcode, &ad) == 0) { |
855 |
m68k_setpc (m68k_getpc () - 2); |
856 |
fpuop_illg (opcode,UNKNOWN_EXTRA); |
857 |
return; |
858 |
} |
859 |
if (CPUType == 4) { |
860 |
/* 68040 */ |
861 |
if (incr < 0) { |
862 |
/* @@@ This may be wrong. */ |
863 |
ad -= 4; |
864 |
d = get_long (ad); |
865 |
if ((d & 0xff000000) != 0) { /* Not a NULL frame? */ |
866 |
if ((d & 0x00ff0000) == 0) { /* IDLE */ |
867 |
} else if ((d & 0x00ff0000) == 0x00300000) { /* UNIMP */ |
868 |
ad -= 44; |
869 |
} else if ((d & 0x00ff0000) == 0x00600000) { /* BUSY */ |
870 |
ad -= 92; |
871 |
} |
872 |
} |
873 |
} else { |
874 |
d = get_long (ad); |
875 |
ad += 4; |
876 |
if ((d & 0xff000000) != 0) { /* Not a NULL frame? */ |
877 |
if ((d & 0x00ff0000) == 0) { /* IDLE */ |
878 |
} else if ((d & 0x00ff0000) == 0x00300000) { /* UNIMP */ |
879 |
ad += 44; |
880 |
} else if ((d & 0x00ff0000) == 0x00600000) { /* BUSY */ |
881 |
ad += 92; |
882 |
} |
883 |
} |
884 |
} |
885 |
} else { |
886 |
if (incr < 0) { |
887 |
ad -= 4; |
888 |
d = get_long (ad); |
889 |
if ((d & 0xff000000) != 0) { |
890 |
if ((d & 0x00ff0000) == 0x00180000) |
891 |
ad -= 6 * 4; |
892 |
else if ((d & 0x00ff0000) == 0x00380000) |
893 |
ad -= 14 * 4; |
894 |
else if ((d & 0x00ff0000) == 0x00b40000) |
895 |
ad -= 45 * 4; |
896 |
} |
897 |
} else { |
898 |
d = get_long (ad); |
899 |
ad += 4; |
900 |
if ((d & 0xff000000) != 0) { |
901 |
if ((d & 0x00ff0000) == 0x00180000) |
902 |
ad += 6 * 4; |
903 |
else if ((d & 0x00ff0000) == 0x00380000) |
904 |
ad += 14 * 4; |
905 |
else if ((d & 0x00ff0000) == 0x00b40000) |
906 |
ad += 45 * 4; |
907 |
} |
908 |
} |
909 |
} |
910 |
if ((opcode & 0x38) == 0x18) |
911 |
m68k_areg (regs, opcode & 7) = ad; |
912 |
if ((opcode & 0x38) == 0x20) |
913 |
m68k_areg (regs, opcode & 7) = ad; |
914 |
} |
915 |
|
916 |
#if USE_LONG_DOUBLE |
917 |
static const fpu_register const_e = 2.7182818284590452353602874713526625L; |
918 |
static const fpu_register const_log10_e = 0.4342944819032518276511289189166051L; |
919 |
static const fpu_register const_loge_10 = 2.3025850929940456840179914546843642L; |
920 |
#else |
921 |
static const fpu_register const_e = 2.7182818284590452354; |
922 |
static const fpu_register const_log10_e = 0.43429448190325182765; |
923 |
static const fpu_register const_loge_10 = 2.30258509299404568402; |
924 |
#endif |
925 |
|
926 |
static const fpu_register power10[] = { |
927 |
1e0, 1e1, 1e2, 1e4, 1e8, 1e16, 1e32, 1e64, 1e128, 1e256 |
928 |
#if USE_LONG_DOUBLE |
929 |
, 1e512, 1e1024, 1e2048, 1e4096 |
930 |
#endif |
931 |
}; |
932 |
|
933 |
/* 128 words, indexed through the low byte of the 68k fpu control word */ |
934 |
static uae_u16 x86_fpucw[]={ |
935 |
0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, /* p0r0 */ |
936 |
0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, /* p0r1 */ |
937 |
0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, /* p0r2 */ |
938 |
0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, /* p0r3 */ |
939 |
|
940 |
0x107f, 0x107f, 0x107f, 0x107f, 0x107f, 0x107f, 0x107f, 0x107f, /* p1r0 */ |
941 |
0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, /* p1r1 */ |
942 |
0x147f, 0x147f, 0x147f, 0x147f, 0x147f, 0x147f, 0x147f, 0x147f, /* p1r2 */ |
943 |
0x187f, 0x187f, 0x187f, 0x187f, 0x187f, 0x187f, 0x187f, 0x187f, /* p1r3 */ |
944 |
|
945 |
0x127f, 0x127f, 0x127f, 0x127f, 0x127f, 0x127f, 0x127f, 0x127f, /* p2r0 */ |
946 |
0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, /* p2r1 */ |
947 |
0x167f, 0x167f, 0x167f, 0x167f, 0x167f, 0x167f, 0x167f, 0x167f, /* p2r2 */ |
948 |
0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, /* p2r3 */ |
949 |
|
950 |
0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, /* p3r0 */ |
951 |
0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, /* p3r1 */ |
952 |
0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, /* p3r2 */ |
953 |
0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f /* p3r3 */ |
954 |
}; |
955 |
|
956 |
void comp_fpp_opp (uae_u32 opcode, uae_u16 extra) |
957 |
{ |
958 |
int reg; |
959 |
int src; |
960 |
|
961 |
switch ((extra >> 13) & 0x7) { |
962 |
case 3: /* 2nd most common */ |
963 |
if (put_fp_value ((extra >> 7)&7 , opcode, extra) < 0) { |
964 |
FAIL(1); |
965 |
return; |
966 |
|
967 |
} |
968 |
return; |
969 |
case 6: |
970 |
case 7: |
971 |
{ |
972 |
uae_u32 ad, list = 0; |
973 |
int incr = 0; |
974 |
if (extra & 0x2000) { |
975 |
uae_u32 ad; |
976 |
|
977 |
/* FMOVEM FPP->memory */ |
978 |
switch ((extra >> 11) & 3) { /* Get out early if failure */ |
979 |
case 0: |
980 |
case 2: |
981 |
break; |
982 |
case 1: |
983 |
case 3: |
984 |
default: |
985 |
FAIL(1); return; |
986 |
} |
987 |
ad=get_fp_ad (opcode, &ad); |
988 |
if (ad<0) { |
989 |
abort(); |
990 |
m68k_setpc (m68k_getpc () - 4); |
991 |
fpuop_illg (opcode,extra); |
992 |
return; |
993 |
} |
994 |
switch ((extra >> 11) & 3) { |
995 |
case 0: /* static pred */ |
996 |
list = extra & 0xff; |
997 |
incr = -1; |
998 |
break; |
999 |
case 2: /* static postinc */ |
1000 |
list = extra & 0xff; |
1001 |
incr = 1; |
1002 |
break; |
1003 |
case 1: /* dynamic pred */ |
1004 |
case 3: /* dynamic postinc */ |
1005 |
abort(); |
1006 |
} |
1007 |
if (incr < 0) { /* Predecrement */ |
1008 |
for (reg = 7; reg >= 0; reg--) { |
1009 |
if (list & 0x80) { |
1010 |
fmov_ext_mr((uintptr)temp_fp,reg); |
1011 |
delay; |
1012 |
sub_l_ri(ad,4); |
1013 |
mov_l_rm(S2,(uintptr)temp_fp); |
1014 |
writelong_clobber(ad,S2,S3); |
1015 |
sub_l_ri(ad,4); |
1016 |
mov_l_rm(S2,(uintptr)temp_fp+4); |
1017 |
writelong_clobber(ad,S2,S3); |
1018 |
sub_l_ri(ad,4); |
1019 |
mov_w_rm(S2,(uintptr)temp_fp+8); |
1020 |
writeword_clobber(ad,S2,S3); |
1021 |
} |
1022 |
list <<= 1; |
1023 |
} |
1024 |
} |
1025 |
else { /* Postincrement */ |
1026 |
for (reg = 0; reg < 8; reg++) { |
1027 |
if (list & 0x80) { |
1028 |
fmov_ext_mr((uintptr)temp_fp,reg); |
1029 |
delay; |
1030 |
mov_w_rm(S2,(uintptr)temp_fp+8); |
1031 |
writeword_clobber(ad,S2,S3); |
1032 |
add_l_ri(ad,4); |
1033 |
mov_l_rm(S2,(uintptr)temp_fp+4); |
1034 |
writelong_clobber(ad,S2,S3); |
1035 |
add_l_ri(ad,4); |
1036 |
mov_l_rm(S2,(uintptr)temp_fp); |
1037 |
writelong_clobber(ad,S2,S3); |
1038 |
add_l_ri(ad,4); |
1039 |
} |
1040 |
list <<= 1; |
1041 |
} |
1042 |
} |
1043 |
if ((opcode & 0x38) == 0x18) |
1044 |
mov_l_rr((opcode & 7)+8,ad); |
1045 |
if ((opcode & 0x38) == 0x20) |
1046 |
mov_l_rr((opcode & 7)+8,ad); |
1047 |
} else { |
1048 |
/* FMOVEM memory->FPP */ |
1049 |
|
1050 |
uae_u32 ad; |
1051 |
switch ((extra >> 11) & 3) { /* Get out early if failure */ |
1052 |
case 0: |
1053 |
case 2: |
1054 |
break; |
1055 |
case 1: |
1056 |
case 3: |
1057 |
default: |
1058 |
FAIL(1); return; |
1059 |
} |
1060 |
ad=get_fp_ad (opcode, &ad); |
1061 |
if (ad<0) { |
1062 |
abort(); |
1063 |
m68k_setpc (m68k_getpc () - 4); |
1064 |
write_log("no ad\n"); |
1065 |
fpuop_illg (opcode,extra); |
1066 |
return; |
1067 |
} |
1068 |
switch ((extra >> 11) & 3) { |
1069 |
case 0: /* static pred */ |
1070 |
list = extra & 0xff; |
1071 |
incr = -1; |
1072 |
break; |
1073 |
case 2: /* static postinc */ |
1074 |
list = extra & 0xff; |
1075 |
incr = 1; |
1076 |
break; |
1077 |
case 1: /* dynamic pred */ |
1078 |
case 3: /* dynamic postinc */ |
1079 |
abort(); |
1080 |
} |
1081 |
|
1082 |
if (incr < 0) { |
1083 |
// not reached |
1084 |
for (reg = 7; reg >= 0; reg--) { |
1085 |
uae_u32 wrd1, wrd2, wrd3; |
1086 |
if (list & 0x80) { |
1087 |
sub_l_ri(ad,4); |
1088 |
readlong(ad,S2,S3); |
1089 |
mov_l_mr((uintptr)(temp_fp),S2); |
1090 |
sub_l_ri(ad,4); |
1091 |
readlong(ad,S2,S3); |
1092 |
mov_l_mr((uintptr)(temp_fp)+4,S2); |
1093 |
sub_l_ri(ad,4); |
1094 |
readword(ad,S2,S3); |
1095 |
mov_w_mr(((uintptr)temp_fp)+8,S2); |
1096 |
delay2; |
1097 |
fmov_ext_rm(reg,(uintptr)(temp_fp)); |
1098 |
} |
1099 |
list <<= 1; |
1100 |
} |
1101 |
} |
1102 |
else { |
1103 |
for (reg = 0; reg < 8; reg++) { |
1104 |
uae_u32 wrd1, wrd2, wrd3; |
1105 |
if (list & 0x80) { |
1106 |
readword(ad,S2,S3); |
1107 |
mov_w_mr(((uintptr)temp_fp)+8,S2); |
1108 |
add_l_ri(ad,4); |
1109 |
readlong(ad,S2,S3); |
1110 |
mov_l_mr((uintptr)(temp_fp)+4,S2); |
1111 |
add_l_ri(ad,4); |
1112 |
readlong(ad,S2,S3); |
1113 |
mov_l_mr((uintptr)(temp_fp),S2); |
1114 |
add_l_ri(ad,4); |
1115 |
delay2; |
1116 |
fmov_ext_rm(reg,(uintptr)(temp_fp)); |
1117 |
} |
1118 |
list <<= 1; |
1119 |
} |
1120 |
} |
1121 |
if ((opcode & 0x38) == 0x18) |
1122 |
mov_l_rr((opcode & 7)+8,ad); |
1123 |
if ((opcode & 0x38) == 0x20) |
1124 |
mov_l_rr((opcode & 7)+8,ad); |
1125 |
} |
1126 |
} |
1127 |
return; |
1128 |
|
1129 |
case 4: |
1130 |
case 5: /* rare */ |
1131 |
if ((opcode & 0x30) == 0) { |
1132 |
if (extra & 0x2000) { |
1133 |
if (extra & 0x1000) { |
1134 |
#if HANDLE_FPCR |
1135 |
mov_l_rm(opcode & 15, (uintptr)&fpu.fpcr.rounding_mode); |
1136 |
or_l_rm(opcode & 15, (uintptr)&fpu.fpcr.rounding_precision); |
1137 |
#else |
1138 |
FAIL(1); |
1139 |
return; |
1140 |
#endif |
1141 |
} |
1142 |
if (extra & 0x0800) { |
1143 |
FAIL(1); |
1144 |
return; |
1145 |
} |
1146 |
if (extra & 0x0400) { |
1147 |
mov_l_rm(opcode & 15,(uintptr)&fpu.instruction_address); |
1148 |
return; |
1149 |
} |
1150 |
} else { |
1151 |
// gb-- moved here so that we may FAIL() without generating any code |
1152 |
if (extra & 0x0800) { |
1153 |
// set_fpsr(m68k_dreg (regs, opcode & 15)); |
1154 |
FAIL(1); |
1155 |
return; |
1156 |
} |
1157 |
if (extra & 0x1000) { |
1158 |
#if HANDLE_FPCR |
1159 |
#if defined(FPU_USE_X86_ROUNDING_MODE) && defined(FPU_USE_X86_ROUNDING_PRECISION) |
1160 |
FAIL(1); |
1161 |
return; |
1162 |
#endif |
1163 |
mov_l_rr(S1,opcode & 15); |
1164 |
mov_l_rr(S2,opcode & 15); |
1165 |
and_l_ri(S1,FPCR_ROUNDING_PRECISION); |
1166 |
and_l_ri(S2,FPCR_ROUNDING_MODE); |
1167 |
mov_l_mr((uintptr)&fpu.fpcr.rounding_precision,S1); |
1168 |
mov_l_mr((uintptr)&fpu.fpcr.rounding_mode,S2); |
1169 |
#else |
1170 |
FAIL(1); |
1171 |
return; |
1172 |
#endif |
1173 |
// return; gb-- FMOVEM could also operate on fpiar |
1174 |
} |
1175 |
if (extra & 0x0400) { |
1176 |
mov_l_mr((uintptr)&fpu.instruction_address,opcode & 15); |
1177 |
// return; gb-- we have to process all FMOVEM bits before returning |
1178 |
} |
1179 |
return; |
1180 |
} |
1181 |
} else if ((opcode & 0x3f) == 0x3c) { |
1182 |
if ((extra & 0x2000) == 0) { |
1183 |
// gb-- moved here so that we may FAIL() without generating any code |
1184 |
if (extra & 0x0800) { |
1185 |
FAIL(1); |
1186 |
return; |
1187 |
} |
1188 |
if (extra & 0x1000) { |
1189 |
uae_u32 val=comp_get_ilong((m68k_pc_offset+=4)-4); |
1190 |
#if HANDLE_FPCR |
1191 |
#if defined(FPU_USE_X86_ROUNDING_MODE) && defined(FPU_USE_X86_ROUNDING_PRECISION) |
1192 |
FAIL(1); |
1193 |
return; |
1194 |
#endif |
1195 |
// mov_l_mi((uintptr)®s.fpcr,val); |
1196 |
mov_l_ri(S1,val); |
1197 |
mov_l_ri(S2,val); |
1198 |
and_l_ri(S1,FPCR_ROUNDING_PRECISION); |
1199 |
and_l_ri(S2,FPCR_ROUNDING_MODE); |
1200 |
mov_l_mr((uintptr)&fpu.fpcr.rounding_precision,S1); |
1201 |
mov_l_mr((uintptr)&fpu.fpcr.rounding_mode,S2); |
1202 |
#else |
1203 |
FAIL(1); |
1204 |
return; |
1205 |
#endif |
1206 |
// return; gb-- FMOVEM could also operate on fpiar |
1207 |
} |
1208 |
if (extra & 0x0400) { |
1209 |
uae_u32 val=comp_get_ilong((m68k_pc_offset+=4)-4); |
1210 |
mov_l_mi((uintptr)&fpu.instruction_address,val); |
1211 |
// return; gb-- we have to process all FMOVEM bits before returning |
1212 |
} |
1213 |
return; |
1214 |
} |
1215 |
FAIL(1); |
1216 |
return; |
1217 |
} else if (extra & 0x2000) { |
1218 |
FAIL(1); |
1219 |
return; |
1220 |
} else { |
1221 |
FAIL(1); |
1222 |
return; |
1223 |
} |
1224 |
FAIL(1); |
1225 |
return; |
1226 |
|
1227 |
case 0: |
1228 |
case 2: /* Extremely common */ |
1229 |
reg = (extra >> 7) & 7; |
1230 |
if ((extra & 0xfc00) == 0x5c00) { |
1231 |
switch (extra & 0x7f) { |
1232 |
case 0x00: |
1233 |
fmov_pi(reg); |
1234 |
break; |
1235 |
case 0x0b: |
1236 |
fmov_log10_2(reg); |
1237 |
break; |
1238 |
case 0x0c: |
1239 |
#if USE_LONG_DOUBLE |
1240 |
fmov_ext_rm(reg,(uintptr)&const_e); |
1241 |
#else |
1242 |
fmov_rm(reg,(uintptr)&const_e); |
1243 |
#endif |
1244 |
break; |
1245 |
case 0x0d: |
1246 |
fmov_log2_e(reg); |
1247 |
break; |
1248 |
case 0x0e: |
1249 |
#if USE_LONG_DOUBLE |
1250 |
fmov_ext_rm(reg,(uintptr)&const_log10_e); |
1251 |
#else |
1252 |
fmov_rm(reg,(uintptr)&const_log10_e); |
1253 |
#endif |
1254 |
break; |
1255 |
case 0x0f: |
1256 |
fmov_0(reg); |
1257 |
break; |
1258 |
case 0x30: |
1259 |
fmov_loge_2(reg); |
1260 |
break; |
1261 |
case 0x31: |
1262 |
#if USE_LONG_DOUBLE |
1263 |
fmov_ext_rm(reg,(uintptr)&const_loge_10); |
1264 |
#else |
1265 |
fmov_rm(reg,(uintptr)&const_loge_10); |
1266 |
#endif |
1267 |
break; |
1268 |
case 0x32: |
1269 |
fmov_1(reg); |
1270 |
break; |
1271 |
case 0x33: |
1272 |
case 0x34: |
1273 |
case 0x35: |
1274 |
case 0x36: |
1275 |
case 0x37: |
1276 |
case 0x38: |
1277 |
case 0x39: |
1278 |
case 0x3a: |
1279 |
case 0x3b: |
1280 |
#if USE_LONG_DOUBLE |
1281 |
case 0x3c: |
1282 |
case 0x3d: |
1283 |
case 0x3e: |
1284 |
case 0x3f: |
1285 |
fmov_ext_rm(reg,(uintptr)(power10+(extra & 0x7f)-0x32)); |
1286 |
#else |
1287 |
fmov_rm(reg,(uintptr)(power10+(extra & 0x7f)-0x32)); |
1288 |
#endif |
1289 |
break; |
1290 |
default: |
1291 |
/* This is not valid, so we fail */ |
1292 |
FAIL(1); |
1293 |
return; |
1294 |
} |
1295 |
return; |
1296 |
} |
1297 |
|
1298 |
switch (extra & 0x7f) { |
1299 |
case 0x00: /* FMOVE */ |
1300 |
case 0x40: /* Explicit rounding. This is just a quick fix. Same |
1301 |
* for all other cases that have three choices */ |
1302 |
case 0x44: |
1303 |
dont_care_fflags(); |
1304 |
src=get_fp_value (opcode, extra); |
1305 |
if (src < 0) { |
1306 |
FAIL(1); /* Illegal instruction */ |
1307 |
return; |
1308 |
} |
1309 |
fmov_rr(reg,src); |
1310 |
MAKE_FPSR (src); |
1311 |
break; |
1312 |
case 0x01: /* FINT */ |
1313 |
FAIL(1); |
1314 |
return; |
1315 |
dont_care_fflags(); |
1316 |
case 0x02: /* FSINH */ |
1317 |
FAIL(1); |
1318 |
return; |
1319 |
dont_care_fflags(); |
1320 |
break; |
1321 |
case 0x03: /* FINTRZ */ |
1322 |
#if USE_X86_FPUCW |
1323 |
/* If we have control over the CW, we can do this */ |
1324 |
dont_care_fflags(); |
1325 |
src=get_fp_value (opcode, extra); |
1326 |
if (src < 0) { |
1327 |
FAIL(1); /* Illegal instruction */ |
1328 |
return; |
1329 |
} |
1330 |
mov_l_ri(S1,16); /* Switch to "round to zero" mode */ |
1331 |
fldcw_m_indexed(S1,(uae_u32)x86_fpucw); |
1332 |
|
1333 |
frndint_rr(reg,src); |
1334 |
|
1335 |
/* restore control word */ |
1336 |
mov_l_rm(S1,(uintptr)®s.fpcr); |
1337 |
and_l_ri(S1,0x000000f0); |
1338 |
fldcw_m_indexed(S1,(uintptr)x86_fpucw); |
1339 |
|
1340 |
MAKE_FPSR (reg); |
1341 |
break; |
1342 |
#endif |
1343 |
FAIL(1); |
1344 |
return; |
1345 |
break; |
1346 |
case 0x04: /* FSQRT */ |
1347 |
case 0x41: |
1348 |
case 0x45: |
1349 |
dont_care_fflags(); |
1350 |
src=get_fp_value (opcode, extra); |
1351 |
if (src < 0) { |
1352 |
FAIL(1); /* Illegal instruction */ |
1353 |
return; |
1354 |
} |
1355 |
fsqrt_rr(reg,src); |
1356 |
MAKE_FPSR (reg); |
1357 |
break; |
1358 |
case 0x06: /* FLOGNP1 */ |
1359 |
FAIL(1); |
1360 |
return; |
1361 |
dont_care_fflags(); |
1362 |
break; |
1363 |
case 0x08: /* FETOXM1 */ |
1364 |
FAIL(1); |
1365 |
return; |
1366 |
dont_care_fflags(); |
1367 |
break; |
1368 |
case 0x09: /* FTANH */ |
1369 |
FAIL(1); |
1370 |
return; |
1371 |
dont_care_fflags(); |
1372 |
break; |
1373 |
case 0x0a: /* FATAN */ |
1374 |
FAIL(1); |
1375 |
return; |
1376 |
dont_care_fflags(); |
1377 |
break; |
1378 |
case 0x0c: /* FASIN */ |
1379 |
FAIL(1); |
1380 |
return; |
1381 |
dont_care_fflags(); |
1382 |
break; |
1383 |
case 0x0d: /* FATANH */ |
1384 |
FAIL(1); |
1385 |
return; |
1386 |
dont_care_fflags(); |
1387 |
break; |
1388 |
case 0x0e: /* FSIN */ |
1389 |
dont_care_fflags(); |
1390 |
src=get_fp_value (opcode, extra); |
1391 |
if (src < 0) { |
1392 |
FAIL(1); /* Illegal instruction */ |
1393 |
return; |
1394 |
} |
1395 |
fsin_rr(reg,src); |
1396 |
MAKE_FPSR (reg); |
1397 |
break; |
1398 |
case 0x0f: /* FTAN */ |
1399 |
FAIL(1); |
1400 |
return; |
1401 |
dont_care_fflags(); |
1402 |
break; |
1403 |
case 0x10: /* FETOX */ |
1404 |
dont_care_fflags(); |
1405 |
src=get_fp_value (opcode, extra); |
1406 |
if (src < 0) { |
1407 |
FAIL(1); /* Illegal instruction */ |
1408 |
return; |
1409 |
} |
1410 |
fetox_rr(reg,src); |
1411 |
MAKE_FPSR (reg); |
1412 |
break; |
1413 |
case 0x11: /* FTWOTOX */ |
1414 |
dont_care_fflags(); |
1415 |
src=get_fp_value (opcode, extra); |
1416 |
if (src < 0) { |
1417 |
FAIL(1); /* Illegal instruction */ |
1418 |
return; |
1419 |
} |
1420 |
ftwotox_rr(reg,src); |
1421 |
MAKE_FPSR (reg); |
1422 |
break; |
1423 |
case 0x12: /* FTENTOX */ |
1424 |
FAIL(1); |
1425 |
return; |
1426 |
dont_care_fflags(); |
1427 |
break; |
1428 |
case 0x14: /* FLOGN */ |
1429 |
FAIL(1); |
1430 |
return; |
1431 |
dont_care_fflags(); |
1432 |
break; |
1433 |
case 0x15: /* FLOG10 */ |
1434 |
FAIL(1); |
1435 |
return; |
1436 |
dont_care_fflags(); |
1437 |
break; |
1438 |
case 0x16: /* FLOG2 */ |
1439 |
dont_care_fflags(); |
1440 |
src=get_fp_value (opcode, extra); |
1441 |
if (src < 0) { |
1442 |
FAIL(1); /* Illegal instruction */ |
1443 |
return; |
1444 |
} |
1445 |
flog2_rr(reg,src); |
1446 |
MAKE_FPSR (reg); |
1447 |
break; |
1448 |
case 0x18: /* FABS */ |
1449 |
case 0x58: |
1450 |
case 0x5c: |
1451 |
dont_care_fflags(); |
1452 |
src=get_fp_value (opcode, extra); |
1453 |
if (src < 0) { |
1454 |
FAIL(1); /* Illegal instruction */ |
1455 |
return; |
1456 |
} |
1457 |
fabs_rr(reg,src); |
1458 |
MAKE_FPSR (reg); |
1459 |
break; |
1460 |
case 0x19: /* FCOSH */ |
1461 |
FAIL(1); |
1462 |
return; |
1463 |
dont_care_fflags(); |
1464 |
break; |
1465 |
case 0x1a: /* FNEG */ |
1466 |
case 0x5a: |
1467 |
case 0x5e: |
1468 |
dont_care_fflags(); |
1469 |
src=get_fp_value (opcode, extra); |
1470 |
if (src < 0) { |
1471 |
FAIL(1); /* Illegal instruction */ |
1472 |
return; |
1473 |
} |
1474 |
fneg_rr(reg,src); |
1475 |
MAKE_FPSR (reg); |
1476 |
break; |
1477 |
case 0x1c: /* FACOS */ |
1478 |
FAIL(1); |
1479 |
return; |
1480 |
dont_care_fflags(); |
1481 |
break; |
1482 |
case 0x1d: /* FCOS */ |
1483 |
dont_care_fflags(); |
1484 |
src=get_fp_value (opcode, extra); |
1485 |
if (src < 0) { |
1486 |
FAIL(1); /* Illegal instruction */ |
1487 |
return; |
1488 |
} |
1489 |
fcos_rr(reg,src); |
1490 |
MAKE_FPSR (reg); |
1491 |
break; |
1492 |
case 0x1e: /* FGETEXP */ |
1493 |
FAIL(1); |
1494 |
return; |
1495 |
dont_care_fflags(); |
1496 |
break; |
1497 |
case 0x1f: /* FGETMAN */ |
1498 |
FAIL(1); |
1499 |
return; |
1500 |
dont_care_fflags(); |
1501 |
break; |
1502 |
case 0x20: /* FDIV */ |
1503 |
case 0x60: |
1504 |
case 0x64: |
1505 |
dont_care_fflags(); |
1506 |
src=get_fp_value (opcode, extra); |
1507 |
if (src < 0) { |
1508 |
FAIL(1); /* Illegal instruction */ |
1509 |
return; |
1510 |
} |
1511 |
fdiv_rr(reg,src); |
1512 |
MAKE_FPSR (reg); |
1513 |
break; |
1514 |
case 0x21: /* FMOD */ |
1515 |
dont_care_fflags(); |
1516 |
src=get_fp_value (opcode, extra); |
1517 |
if (src < 0) { |
1518 |
FAIL(1); /* Illegal instruction */ |
1519 |
return; |
1520 |
} |
1521 |
frem_rr(reg,src); |
1522 |
MAKE_FPSR (reg); |
1523 |
break; |
1524 |
case 0x22: /* FADD */ |
1525 |
case 0x62: |
1526 |
case 0x66: |
1527 |
dont_care_fflags(); |
1528 |
src=get_fp_value (opcode, extra); |
1529 |
if (src < 0) { |
1530 |
FAIL(1); /* Illegal instruction */ |
1531 |
return; |
1532 |
} |
1533 |
fadd_rr(reg,src); |
1534 |
MAKE_FPSR (reg); |
1535 |
break; |
1536 |
case 0x23: /* FMUL */ |
1537 |
case 0x63: |
1538 |
case 0x67: |
1539 |
dont_care_fflags(); |
1540 |
src=get_fp_value (opcode, extra); |
1541 |
if (src < 0) { |
1542 |
FAIL(1); /* Illegal instruction */ |
1543 |
return; |
1544 |
} |
1545 |
fmul_rr(reg,src); |
1546 |
MAKE_FPSR (reg); |
1547 |
break; |
1548 |
case 0x24: /* FSGLDIV */ |
1549 |
dont_care_fflags(); |
1550 |
src=get_fp_value (opcode, extra); |
1551 |
if (src < 0) { |
1552 |
FAIL(1); /* Illegal instruction */ |
1553 |
return; |
1554 |
} |
1555 |
fdiv_rr(reg,src); |
1556 |
MAKE_FPSR (reg); |
1557 |
break; |
1558 |
case 0x25: /* FREM */ |
1559 |
// gb-- disabled because the quotient byte must be computed |
1560 |
// otherwise, free rotation in ClarisWorks doesn't work. |
1561 |
FAIL(1); |
1562 |
return; |
1563 |
dont_care_fflags(); |
1564 |
src=get_fp_value (opcode, extra); |
1565 |
if (src < 0) { |
1566 |
FAIL(1); /* Illegal instruction */ |
1567 |
return; |
1568 |
} |
1569 |
frem1_rr(reg,src); |
1570 |
MAKE_FPSR (reg); |
1571 |
break; |
1572 |
case 0x26: /* FSCALE */ |
1573 |
dont_care_fflags(); |
1574 |
FAIL(1); |
1575 |
return; |
1576 |
break; |
1577 |
case 0x27: /* FSGLMUL */ |
1578 |
dont_care_fflags(); |
1579 |
src=get_fp_value (opcode, extra); |
1580 |
if (src < 0) { |
1581 |
FAIL(1); /* Illegal instruction */ |
1582 |
return; |
1583 |
} |
1584 |
fmul_rr(reg,src); |
1585 |
MAKE_FPSR (reg); |
1586 |
break; |
1587 |
case 0x28: /* FSUB */ |
1588 |
case 0x68: |
1589 |
case 0x6c: |
1590 |
dont_care_fflags(); |
1591 |
src=get_fp_value (opcode, extra); |
1592 |
if (src < 0) { |
1593 |
FAIL(1); /* Illegal instruction */ |
1594 |
return; |
1595 |
} |
1596 |
fsub_rr(reg,src); |
1597 |
MAKE_FPSR (reg); |
1598 |
break; |
1599 |
case 0x30: /* FSINCOS */ |
1600 |
case 0x31: |
1601 |
case 0x32: |
1602 |
case 0x33: |
1603 |
case 0x34: |
1604 |
case 0x35: |
1605 |
case 0x36: |
1606 |
case 0x37: |
1607 |
FAIL(1); |
1608 |
return; |
1609 |
dont_care_fflags(); |
1610 |
break; |
1611 |
case 0x38: /* FCMP */ |
1612 |
src=get_fp_value (opcode, extra); |
1613 |
if (src < 0) { |
1614 |
FAIL(1); /* Illegal instruction */ |
1615 |
return; |
1616 |
} |
1617 |
fmov_rr(FP_RESULT,reg); |
1618 |
fsub_rr(FP_RESULT,src); /* Right way? */ |
1619 |
break; |
1620 |
case 0x3a: /* FTST */ |
1621 |
src=get_fp_value (opcode, extra); |
1622 |
if (src < 0) { |
1623 |
FAIL(1); /* Illegal instruction */ |
1624 |
return; |
1625 |
} |
1626 |
fmov_rr(FP_RESULT,src); |
1627 |
break; |
1628 |
default: |
1629 |
FAIL(1); |
1630 |
return; |
1631 |
break; |
1632 |
} |
1633 |
return; |
1634 |
} |
1635 |
m68k_setpc (m68k_getpc () - 4); |
1636 |
fpuop_illg (opcode,extra); |
1637 |
} |