macemu/BasiliskII/src/uae_cpu_2021/compiler/compemu_fpp.cpp

2090 lines
43 KiB
C++

/*
* compiler/compemu_fpp.cpp - Dynamic translation of FPU instructions
*
* Copyright (c) 2001-2004 Milan Jurik of ARAnyM dev team (see AUTHORS)
*
* Inspired by Christian Bauer's Basilisk II
*
* This file is part of the ARAnyM project which builds a new and powerful
* TOS/FreeMiNT compatible virtual machine running on almost any hardware.
*
* JIT compiler m68k -> IA-32 and AMD64
*
* Original 68040 JIT compiler for UAE, copyright 2000-2002 Bernd Meyer
* Adaptation for Basilisk II and improvements, copyright 2000-2004 Gwenole Beauchesne
* Portions related to CPU detection come from linux/arch/i386/kernel/setup.c
*
* ARAnyM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ARAnyM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ARAnyM; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* UAE - The Un*x Amiga Emulator
*
* MC68881 emulation
*
* Copyright 1996 Herman ten Brugge
* Adapted for JIT compilation (c) Bernd Meyer, 2000
*/
#ifdef USE_JIT
#include "sysdeps.h"
#include <cmath>
#include <cstdio>
#include <cassert>
#include "memory.h"
#include "readcpu.h"
#include "newcpu.h"
#include "main.h"
#include "compiler/compemu.h"
#include "fpu/fpu.h"
#include "fpu/flags.h"
#include "fpu/exceptions.h"
#include "fpu/rounding.h"
#define DEBUG 0
#include "debug.h"
struct jit_disable_opcodes jit_disable;
#if defined(USE_LONG_DOUBLE) || defined(USE_QUAD_DOUBLE)
#define LD(x) x ## L
#else
#define LD(x) x
#endif
// gb-- WARNING: get_fpcr() and set_fpcr() support is experimental
#define HANDLE_FPCR 0
// - IEEE-based fpu core must be used
#if defined(FPU_IEEE)
# define CAN_HANDLE_FPCR
#endif
// - Generic rounding mode and precision modes are supported if set together
#if defined(FPU_USE_GENERIC_ROUNDING_MODE) && defined(FPU_USE_GENERIC_ROUNDING_PRECISION)
# define CAN_HANDLE_FPCR
#endif
// - X86 rounding mode and precision modes are *not* supported but might work (?!)
#if defined(FPU_USE_X86_ROUNDING_MODE) && defined(FPU_USE_X86_ROUNDING_PRECISION)
# define CAN_HANDLE_FPCR
#endif
#if HANDLE_FPCR && !defined(CAN_HANDLE_FPCR)
# warning "Can't handle FPCR, will FAIL(1) at runtime"
# undef HANDLE_FPCR
# define HANDLE_FPCR 0
#endif
#define STATIC_INLINE static inline
#define MAKE_FPSR(r) do { fmov_rr(FP_RESULT,r); } while (0)
#define delay nop() ;nop()
#define delay2 nop() ;nop()
#define UNKNOWN_EXTRA 0xFFFFFFFF
#if 0
static void fpuop_illg(uae_u32 opcode, uae_u32 /* extra */)
{
/*
if (extra == UNKNOWN_EXTRA)
printf("FPU opcode %x, extra UNKNOWN_EXTRA\n",opcode & 0xFFFF);
else
printf("FPU opcode %x, extra %x\n",opcode & 0xFFFF,extra & 0xFFFF);
*/
op_illg(opcode);
}
#endif
uae_s32 temp_fp[4]; /* To convert between FP/integer */
/* return register number, or -1 for failure */
STATIC_INLINE int get_fp_value(uae_u32 opcode, uae_u16 extra)
{
int size;
int mode;
int reg;
uae_u32 ad = 0;
static int const sz1[8] = { 4, 4, 12, 12, 2, 8, 1, 0 };
static int const sz2[8] = { 4, 4, 12, 12, 2, 8, 2, 0 };
if ((extra & 0x4000) == 0)
{
return ((extra >> 10) & 7);
}
mode = (opcode >> 3) & 7;
reg = opcode & 7;
size = (extra >> 10) & 7;
switch (mode)
{
case 0: /* Dn */
switch (size)
{
case 6: /* byte */
sign_extend_8_rr(S1, reg);
mov_l_mr((uintptr) temp_fp, S1);
delay2;
fmovi_rm(FS1, (uintptr) temp_fp);
return FS1;
case 4: /* word */
sign_extend_16_rr(S1, reg);
mov_l_mr((uintptr) temp_fp, S1);
delay2;
fmovi_rm(FS1, (uintptr) temp_fp);
return FS1;
case 0: /* long */
mov_l_mr((uintptr) temp_fp, reg);
delay2;
fmovi_rm(FS1, (uintptr) temp_fp);
return FS1;
case 1: /* single precision */
mov_l_mr((uintptr) temp_fp, reg);
delay2;
fmovs_rm(FS1, (uintptr) temp_fp);
return FS1;
default:
return -1;
}
return -1; /* Should be unreachable */
case 1: /* An */
return -1; /* Genuine invalid instruction */
default:
break;
}
/* OK, we *will* have to load something from an address. Let's make
sure we know how to handle that, or quit early --- i.e. *before*
we do any postincrement/predecrement that we may regret */
switch (size)
{
case 0: /* long */
case 1: /* single precision */
case 2: /* extended precision */
case 4: /* word */
case 5: /* double precision */
case 6: /* byte */
break;
case 3: /* packed decimal static */
default:
return -1;
}
switch (mode)
{
case 2: /* (An) */
ad = S1; /* We will change it, anyway ;-) */
mov_l_rr(ad, reg + 8);
break;
case 3: /* (An)+ */
ad = S1;
mov_l_rr(ad, reg + 8);
lea_l_brr(reg + 8, reg + 8, (reg == 7 ? sz2[size] : sz1[size]));
break;
case 4: /* -(An) */
ad = S1;
lea_l_brr(reg + 8, reg + 8, -(reg == 7 ? sz2[size] : sz1[size]));
mov_l_rr(ad, reg + 8);
break;
case 5: /* d16(An) */
{
uae_u32 off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
mov_l_rr(ad, reg + 8);
lea_l_brr(ad, ad, off);
}
break;
case 6: /* d8(An,Xn) */
{
uae_u32 dp = comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
calc_disp_ea_020(reg + 8, dp, ad, S2);
}
break;
case 7:
switch (reg)
{
case 0: /* abs.w */
{
uae_u32 off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
mov_l_ri(ad, off);
}
break;
case 1: /* abs.l */
{
uae_u32 off = comp_get_ilong((m68k_pc_offset += 4) - 4);
ad = S1;
mov_l_ri(ad, off);
}
break;
case 2: /* d16(pc) */
{
uae_u32 address = start_pc + ((char *) comp_pc_p - (char *) start_pc_p) + m68k_pc_offset;
uae_s32 PC16off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
mov_l_ri(ad, address + PC16off);
}
break;
case 3: /* d8(pc,Xn) */
return -1;
case 4: /* #imm */
{
uae_u32 address = start_pc + ((char *) comp_pc_p - (char *) start_pc_p) + m68k_pc_offset;
ad = S1;
// Immediate addressing mode && Operation Length == Byte ->
// Use the low-order byte of the extension word.
if (size == 6)
address++;
mov_l_ri(ad, address);
m68k_pc_offset += sz2[size];
}
break;
default:
return -1;
}
}
switch (size)
{
case 0: /* long */
readlong(ad, S2, S3);
mov_l_mr((uintptr) temp_fp, S2);
delay2;
fmovi_rm(FS1, (uintptr) temp_fp);
break;
case 1: /* single precision */
readlong(ad, S2, S3);
mov_l_mr((uintptr) temp_fp, S2);
delay2;
fmovs_rm(FS1, (uintptr) temp_fp);
break;
case 2: /* extended precision */
readword(ad, S2, S3);
mov_w_mr(((uintptr) temp_fp) + 8, S2);
add_l_ri(ad, 4);
readlong(ad, S2, S3);
// always set the explicit integer bit.
or_l_ri(S2, 0x80000000);
mov_l_mr((uintptr) (temp_fp) + 4, S2);
add_l_ri(ad, 4);
readlong(ad, S2, S3);
mov_l_mr((uintptr) (temp_fp), S2);
delay2;
fmov_ext_rm(FS1, (uintptr) (temp_fp));
break;
case 3: /* packed decimal static */
return -1; /* Some silly "packed" stuff */
case 4: /* word */
readword(ad, S2, S3);
sign_extend_16_rr(S2, S2);
mov_l_mr((uintptr) temp_fp, S2);
delay2;
fmovi_rm(FS1, (uintptr) temp_fp);
break;
case 5: /* double precision */
readlong(ad, S2, S3);
mov_l_mr(((uintptr) temp_fp) + 4, S2);
add_l_ri(ad, 4);
readlong(ad, S2, S3);
mov_l_mr((uintptr) (temp_fp), S2);
delay2;
fmov_rm(FS1, (uintptr) (temp_fp));
break;
case 6: /* byte */
readbyte(ad, S2, S3);
sign_extend_8_rr(S2, S2);
mov_l_mr((uintptr) temp_fp, S2);
delay2;
fmovi_rm(FS1, (uintptr) temp_fp);
break;
default:
return -1;
}
return FS1;
}
/* return of -1 means failure, >=0 means OK */
STATIC_INLINE int put_fp_value(int val, uae_u32 opcode, uae_u16 extra)
{
int size;
int mode;
int reg;
uae_u32 ad;
static int const sz1[8] = { 4, 4, 12, 12, 2, 8, 1, 0 };
static int const sz2[8] = { 4, 4, 12, 12, 2, 8, 2, 0 };
if ((extra & 0x4000) == 0)
{
const int dest_reg = (extra >> 10) & 7;
fmov_rr(dest_reg, val);
// gb-- status register is affected
MAKE_FPSR(dest_reg);
return 0;
}
mode = (opcode >> 3) & 7;
reg = opcode & 7;
size = (extra >> 10) & 7;
ad = (uae_u32) -1;
switch (mode)
{
case 0: /* Dn */
switch (size)
{
case 6: /* byte */
fmovi_mr((uintptr) temp_fp, val);
delay;
mov_b_rm(reg, (uintptr) temp_fp);
return 0;
case 4: /* word */
fmovi_mr((uintptr) temp_fp, val);
delay;
mov_w_rm(reg, (uintptr) temp_fp);
return 0;
case 0: /* long */
fmovi_mr((uintptr) temp_fp, val);
delay;
mov_l_rm(reg, (uintptr) temp_fp);
return 0;
case 1: /* single precision */
fmovs_mr((uintptr) temp_fp, val);
delay;
mov_l_rm(reg, (uintptr) temp_fp);
return 0;
default:
return -1;
}
case 1: /* An */
return -1; /* genuine invalid instruction */
default:
break;
}
/* Let's make sure we get out *before* doing something silly if
we can't handle the size */
switch (size)
{
case 0: /* long */
case 1: /* single precision */
case 2: /* extended precision */
case 4: /* word */
case 5: /* double precision */
case 6: /* byte */
break;
case 3: /* packed decimal static */
default:
return -1;
}
switch (mode)
{
case 2: /* (An) */
ad = S1;
mov_l_rr(ad, reg + 8);
break;
case 3: /* (An)+ */
ad = S1;
mov_l_rr(ad, reg + 8);
lea_l_brr(reg + 8, reg + 8, (reg == 7 ? sz2[size] : sz1[size]));
break;
case 4: /* -(An) */
ad = S1;
lea_l_brr(reg + 8, reg + 8, -(reg == 7 ? sz2[size] : sz1[size]));
mov_l_rr(ad, reg + 8);
break;
case 5: /* d16(An) */
{
uae_u32 off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
mov_l_rr(ad, reg + 8);
add_l_ri(ad, off);
}
break;
case 6: /* d8(An,Xn) */
{
uae_u32 dp = comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
calc_disp_ea_020(reg + 8, dp, ad, S2);
}
break;
case 7:
switch (reg)
{
case 0: /* abs.w */
{
uae_u32 off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
mov_l_ri(ad, off);
}
break;
case 1: /* abs.l */
{
uae_u32 off = comp_get_ilong((m68k_pc_offset += 4) - 4);
ad = S1;
mov_l_ri(ad, off);
}
break;
case 2: /* d16(pc) */
{
uae_u32 address = start_pc + ((char *) comp_pc_p - (char *) start_pc_p) + m68k_pc_offset;
uae_s32 PC16off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
ad = S1;
mov_l_ri(ad, address + PC16off);
}
break;
case 3: /* d8(pc,Xn) */
return -1;
case 4: /* #imm */
{
uae_u32 address = start_pc + ((char *) comp_pc_p - (char *) start_pc_p) + m68k_pc_offset;
ad = S1;
mov_l_ri(ad, address);
m68k_pc_offset += sz2[size];
}
break;
default:
return -1;
}
}
switch (size)
{
case 0: /* long */
fmovi_mr((uintptr) temp_fp, val);
delay;
mov_l_rm(S2, (uintptr) temp_fp);
writelong_clobber(ad, S2, S3);
break;
case 1: /* single precision */
fmovs_mr((uintptr) temp_fp, val);
delay;
mov_l_rm(S2, (uintptr) temp_fp);
writelong_clobber(ad, S2, S3);
break;
case 2: /* extended precision */
fmov_ext_mr((uintptr) temp_fp, val);
delay;
mov_w_rm(S2, (uintptr) temp_fp + 8);
writeword_clobber(ad, S2, S3);
add_l_ri(ad, 4);
mov_l_rm(S2, (uintptr) temp_fp + 4);
writelong_clobber(ad, S2, S3);
add_l_ri(ad, 4);
mov_l_rm(S2, (uintptr) temp_fp);
writelong_clobber(ad, S2, S3);
break;
case 3: /* packed decimal static */
return -1; /* Packed */
case 4: /* word */
fmovi_mr((uintptr) temp_fp, val);
delay;
mov_l_rm(S2, (uintptr) temp_fp);
writeword_clobber(ad, S2, S3);
break;
case 5: /* double precision */
fmov_mr((uintptr) temp_fp, val);
delay;
mov_l_rm(S2, (uintptr) temp_fp + 4);
writelong_clobber(ad, S2, S3);
add_l_ri(ad, 4);
mov_l_rm(S2, (uintptr) temp_fp);
writelong_clobber(ad, S2, S3);
break;
case 6: /* byte */
fmovi_mr((uintptr) temp_fp, val);
delay;
mov_l_rm(S2, (uintptr) temp_fp);
writebyte(ad, S2, S3);
break;
default:
return -1;
}
return 0;
}
/* return -1 for failure, or register number for success */
STATIC_INLINE int get_fp_ad(uae_u32 opcode)
{
int mode;
int reg;
uae_s32 off;
mode = (opcode >> 3) & 7;
reg = opcode & 7;
switch (mode)
{
case 0: /* Dn */
case 1: /* An */
return -1;
case 2: /* (An) */
case 3: /* (An)+ */
case 4: /* -(An) */
mov_l_rr(S1, 8 + reg);
return S1;
case 5: /* d16(An) */
off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
mov_l_rr(S1, 8 + reg);
add_l_ri(S1, off);
return S1;
case 6: /* d8(An,Xn) */
return -1;
break;
case 7:
switch (reg)
{
case 0: /* abs.w */
off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
mov_l_ri(S1, off);
return S1;
case 1: /* abs.l */
off = comp_get_ilong((m68k_pc_offset += 4) - 4);
mov_l_ri(S1, off);
return S1;
case 2: /* d16(pc) */
off = start_pc + ((char *) comp_pc_p - (char *) start_pc_p) + m68k_pc_offset;
off += (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
mov_l_ri(S1, off);
return S1;
case 3: /* d8(pc,Xn) */
return -1;
default:
return -1;
}
}
abort();
}
/* return -1 for failure, or register number for success */
void comp_fdbcc_opp (uae_u32 /* opcode */, uae_u16 /* extra */)
{
if (jit_disable.fdbcc)
{
FAIL(1);
return;
}
FAIL(1);
return;
}
void comp_fscc_opp(uae_u32 opcode, uae_u16 extra)
{
int reg;
if (jit_disable.fscc)
{
FAIL(1);
return;
}
if (extra & 0x20)
{ /* only cc from 00 to 1f are defined */
FAIL(1);
return;
}
if ((opcode & 0x38) != 0)
{ /* We can only do to integer register */
FAIL(1);
return;
}
fflags_into_flags(S2);
reg = (opcode & 7);
mov_l_ri(S1, 255);
mov_l_ri(S4, 0);
switch (extra & 0x0f)
{ /* according to fpp.c, the 0x10 bit is ignored
*/
case 0:
break; /* set never */
case 1:
mov_l_rr(S2, S4);
cmov_l_rr(S4, S1, 4);
cmov_l_rr(S4, S2, 10);
break;
case 2:
cmov_l_rr(S4, S1, 7);
break;
case 3:
cmov_l_rr(S4, S1, 3);
break;
case 4:
mov_l_rr(S2, S4);
cmov_l_rr(S4, S1, 2);
cmov_l_rr(S4, S2, 10);
break;
case 5:
mov_l_rr(S2, S4);
cmov_l_rr(S4, S1, 6);
cmov_l_rr(S4, S2, 10);
break;
case 6:
cmov_l_rr(S4, S1, 5);
break;
case 7:
cmov_l_rr(S4, S1, 11);
break;
case 8:
cmov_l_rr(S4, S1, 10);
break;
case 9:
cmov_l_rr(S4, S1, 4);
break;
case 10:
cmov_l_rr(S4, S1, 10);
cmov_l_rr(S4, S1, 7);
break;
case 11:
cmov_l_rr(S4, S1, 4);
cmov_l_rr(S4, S1, 3);
break;
case 12:
cmov_l_rr(S4, S1, 2);
break;
case 13:
cmov_l_rr(S4, S1, 6);
break;
case 14:
cmov_l_rr(S4, S1, 5);
cmov_l_rr(S4, S1, 10);
break;
case 15:
mov_l_rr(S4, S1);
break;
}
if ((opcode & 0x38) == 0)
{
mov_b_rr(reg, S4);
} else
{
abort();
#if 0
int cc;
if (get_fp_ad(opcode) < 0)
{
FAIL(1);
} else
{
put_byte(ad, cc ? 0xff : 0x00);
}
#endif
}
}
void comp_ftrapcc_opp (uae_u32 /* opcode */, uaecptr /* oldpc */)
{
FAIL(1);
return;
}
void comp_fbcc_opp(uae_u32 opcode)
{
uae_u32 start_68k_offset = m68k_pc_offset;
uae_u32 off;
uae_u32 v1;
uae_u32 v2;
int cc;
// comp_pc_p is expected to be bound to 32-bit addresses
assert((uintptr) comp_pc_p <= 0xffffffffUL);
if (jit_disable.fbcc)
{
FAIL(1);
return;
}
if (opcode & 0x20)
{ /* only cc from 00 to 1f are defined */
FAIL(1);
return;
}
if ((opcode & 0x40) == 0)
{
off = (uae_s32) (uae_s16) comp_get_iword((m68k_pc_offset += 2) - 2);
} else
{
off = comp_get_ilong((m68k_pc_offset += 4) - 4);
}
mov_l_ri(S1, (uintptr) (comp_pc_p + off - (m68k_pc_offset - start_68k_offset)));
mov_l_ri(PC_P, (uintptr) comp_pc_p);
/* Now they are both constant. Might as well fold in m68k_pc_offset */
add_l_ri(S1, m68k_pc_offset);
add_l_ri(PC_P, m68k_pc_offset);
m68k_pc_offset = 0;
/* according to fpp.c, the 0x10 bit is ignored
(it handles exception handling, which we don't
do, anyway ;-) */
cc = opcode & 0x0f;
v1 = get_const(PC_P);
v2 = get_const(S1);
fflags_into_flags(S2);
switch (cc)
{
case 0:
break; /* jump never */
case 1:
mov_l_rr(S2, PC_P);
cmov_l_rr(PC_P, S1, 4);
cmov_l_rr(PC_P, S2, 10);
break;
case 2:
register_branch(v1, v2, 7);
break;
case 3:
register_branch(v1, v2, 3);
break;
case 4:
mov_l_rr(S2, PC_P);
cmov_l_rr(PC_P, S1, 2);
cmov_l_rr(PC_P, S2, 10);
break;
case 5:
mov_l_rr(S2, PC_P);
cmov_l_rr(PC_P, S1, 6);
cmov_l_rr(PC_P, S2, 10);
break;
case 6:
register_branch(v1, v2, 5);
break;
case 7:
register_branch(v1, v2, 11);
break;
case 8:
register_branch(v1, v2, 10);
break;
case 9:
register_branch(v1, v2, 4);
break;
case 10:
cmov_l_rr(PC_P, S1, 10);
cmov_l_rr(PC_P, S1, 7);
break;
case 11:
cmov_l_rr(PC_P, S1, 4);
cmov_l_rr(PC_P, S1, 3);
break;
case 12:
register_branch(v1, v2, 2);
break;
case 13:
register_branch(v1, v2, 6);
break;
case 14:
cmov_l_rr(PC_P, S1, 5);
cmov_l_rr(PC_P, S1, 10);
break;
case 15:
mov_l_rr(PC_P, S1);
break;
}
}
/* Floating point conditions
The "NotANumber" part could be problematic; Howver, when NaN is
encountered, the ftst instruction sets bot N and Z to 1 on the x87,
so quite often things just fall into place. This is probably not
accurate wrt the 68k FPU, but it is *as* accurate as this was before.
However, some more thought should go into fixing this stuff up so
it accurately emulates the 68k FPU.
>=<U
0000 0x00: 0 --- Never jump
0101 0x01: Z --- jump if zero (x86: 4)
1000 0x02: !(NotANumber || Z || N) --- Neither Z nor N set (x86: 7)
1101 0x03: Z || !(NotANumber || N); --- Z or !N (x86: 4 and 3)
0010 0x04: N && !(NotANumber || Z); --- N and !Z (x86: hard!)
0111 0x05: Z || (N && !NotANumber); --- Z or N (x86: 6)
1010 0x06: !(NotANumber || Z); --- not Z (x86: 5)
1110 0x07: !NotANumber; --- not NaN (x86: 11, not parity)
0001 0x08: NotANumber; --- NaN (x86: 10)
0101 0x09: NotANumber || Z; --- Z (x86: 4)
1001 0x0a: NotANumber || !(N || Z); --- NaN or neither N nor Z (x86: 10 and 7)
1101 0x0b: NotANumber || Z || !N; --- Z or !N (x86: 4 and 3)
0011 0x0c: NotANumber || (N && !Z); --- N (x86: 2)
0111 0x0d: NotANumber || Z || N; --- Z or N (x86: 6)
1010 0x0e: !Z; --- not Z (x86: 5)
1111 0x0f: 1; --- always
This is not how the 68k handles things, though --- it sets Z to 0 and N
to the NaN's sign.... ('o' and 'i' denote differences from the above
table)
>=<U
0000 0x00: 0 --- Never jump
010o 0x01: Z --- jump if zero (x86: 4, not 10)
1000 0x02: !(NotANumber || Z || N) --- Neither Z nor N set (x86: 7)
110o 0x03: Z || !(NotANumber || N); --- Z or !N (x86: 3)
0010 0x04: N && !(NotANumber || Z); --- N and !Z (x86: 2, not 10)
011o 0x05: Z || (N && !NotANumber); --- Z or N (x86: 6, not 10)
1010 0x06: !(NotANumber || Z); --- not Z (x86: 5)
1110 0x07: !NotANumber; --- not NaN (x86: 11, not parity)
0001 0x08: NotANumber; --- NaN (x86: 10)
0101 0x09: NotANumber || Z; --- Z (x86: 4)
1001 0x0a: NotANumber || !(N || Z); --- NaN or neither N nor Z (x86: 10 and 7)
1101 0x0b: NotANumber || Z || !N; --- Z or !N (x86: 4 and 3)
0011 0x0c: NotANumber || (N && !Z); --- N (x86: 2)
0111 0x0d: NotANumber || Z || N; --- Z or N (x86: 6)
101i 0x0e: !Z; --- not Z (x86: 5 and 10)
1111 0x0f: 1; --- always
Of course, this *still* doesn't mean that the x86 and 68k conditions are
equivalent --- the handling of infinities is different, for one thing.
On the 68k, +infinity minus +infinity is NotANumber (as it should be). On
the x86, it is +infinity, and some exception is raised (which I suspect
is promptly ignored) STUPID!
The more I learn about their CPUs, the more I detest Intel....
You can see this in action if you have "Benoit" (see Aminet) and
set the exponent to 16. Wait for a long time, and marvel at the extra black
areas outside the center one. That's where Benoit expects NaN, and the x86
gives +infinity. [Ooops --- that must have been some kind of bug in my code.
it no longer happens, and the resulting graphic looks much better, too]
x86 conditions
0011 : 2
1100 : 3
0101 : 4
1010 : 5
0111 : 6
1000 : 7
0001 : 10
1110 : 11
*/
void comp_fsave_opp(uae_u32 opcode)
{
int incr = (opcode & 0x38) == 0x20 ? -1 : 1;
int i;
int ad;
if (jit_disable.fsave)
{
FAIL(1);
return;
}
FAIL(1);
return;
if ((ad = get_fp_ad(opcode)) < 0)
{
FAIL(1);
return;
}
if (CPUType == 4)
{
/* 4 byte 68040 IDLE frame. */
if (incr < 0)
{
ad -= 4;
put_long(ad, 0x41000000);
} else
{
put_long(ad, 0x41000000);
ad += 4;
}
} else
{
if (incr < 0)
{
ad -= 4;
put_long(ad, 0x70000000);
for (i = 0; i < 5; i++)
{
ad -= 4;
put_long(ad, 0x00000000);
}
ad -= 4;
put_long(ad, 0x1f180000);
} else
{
put_long(ad, 0x1f180000);
ad += 4;
for (i = 0; i < 5; i++)
{
put_long(ad, 0x00000000);
ad += 4;
}
put_long(ad, 0x70000000);
ad += 4;
}
}
if ((opcode & 0x38) == 0x18)
m68k_areg(regs, opcode & 7) = ad;
if ((opcode & 0x38) == 0x20)
m68k_areg(regs, opcode & 7) = ad;
}
void comp_frestore_opp(uae_u32 opcode)
{
uae_u32 d;
int incr = (opcode & 0x38) == 0x20 ? -1 : 1;
int ad;
if (jit_disable.frestore)
{
FAIL(1);
return;
}
FAIL(1);
return;
if ((ad = get_fp_ad(opcode)) < 0)
{
FAIL(1);
return;
}
if (CPUType == 4)
{
/* 68040 */
if (incr < 0)
{
/* @@@ This may be wrong. */
ad -= 4;
d = get_long(ad);
if ((d & 0xff000000) != 0)
{ /* Not a NULL frame? */
if ((d & 0x00ff0000) == 0)
{ /* IDLE */
} else if ((d & 0x00ff0000) == 0x00300000)
{ /* UNIMP */
ad -= 44;
} else if ((d & 0x00ff0000) == 0x00600000)
{ /* BUSY */
ad -= 92;
}
}
} else
{
d = get_long(ad);
ad += 4;
if ((d & 0xff000000) != 0)
{ /* Not a NULL frame? */
if ((d & 0x00ff0000) == 0)
{ /* IDLE */
} else if ((d & 0x00ff0000) == 0x00300000)
{ /* UNIMP */
ad += 44;
} else if ((d & 0x00ff0000) == 0x00600000)
{ /* BUSY */
ad += 92;
}
}
}
} else
{
if (incr < 0)
{
ad -= 4;
d = get_long(ad);
if ((d & 0xff000000) != 0)
{
if ((d & 0x00ff0000) == 0x00180000)
ad -= 6 * 4;
else if ((d & 0x00ff0000) == 0x00380000)
ad -= 14 * 4;
else if ((d & 0x00ff0000) == 0x00b40000)
ad -= 45 * 4;
}
} else
{
d = get_long(ad);
ad += 4;
if ((d & 0xff000000) != 0)
{
if ((d & 0x00ff0000) == 0x00180000)
ad += 6 * 4;
else if ((d & 0x00ff0000) == 0x00380000)
ad += 14 * 4;
else if ((d & 0x00ff0000) == 0x00b40000)
ad += 45 * 4;
}
}
}
if ((opcode & 0x38) == 0x18)
m68k_areg(regs, opcode & 7) = ad;
if ((opcode & 0x38) == 0x20)
m68k_areg(regs, opcode & 7) = ad;
}
#if defined(USE_LONG_DOUBLE) || defined(USE_QUAD_DOUBLE)
static const fpu_register const_e = LD(2.7182818284590452353); // LD(2.7182818284590452353602874713526625);
static const fpu_register const_log10_e = LD(0.4342944819032518276511289189166051);
static const fpu_register const_loge_10 = LD(2.3025850929940456840179914546843642);
#else
static const fpu_register const_e = 2.7182818284590452354;
static const fpu_register const_log10_e = 0.43429448190325182765;
static const fpu_register const_loge_10 = 2.30258509299404568402;
#endif
static const fpu_register power10[] = {
LD(1e0), LD(1e1), LD(1e2), LD(1e4), LD(1e8), LD(1e16), LD(1e32), LD(1e64), LD(1e128), LD(1e256)
#if defined(USE_LONG_DOUBLE) || defined(USE_QUAD_DOUBLE)
, LD(1e512), LD(1e1024), LD(1e2048), LD(1e4096)
#endif
};
/* 128 words, indexed through the low byte of the 68k fpu control word */
#if 0
/* unused*/
static uae_u16 x86_fpucw[]={
0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, /* p0r0 */
0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, /* p0r1 */
0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, /* p0r2 */
0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, /* p0r3 */
0x107f, 0x107f, 0x107f, 0x107f, 0x107f, 0x107f, 0x107f, 0x107f, /* p1r0 */
0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, 0x1c7f, /* p1r1 */
0x147f, 0x147f, 0x147f, 0x147f, 0x147f, 0x147f, 0x147f, 0x147f, /* p1r2 */
0x187f, 0x187f, 0x187f, 0x187f, 0x187f, 0x187f, 0x187f, 0x187f, /* p1r3 */
0x127f, 0x127f, 0x127f, 0x127f, 0x127f, 0x127f, 0x127f, 0x127f, /* p2r0 */
0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, 0x1e7f, /* p2r1 */
0x167f, 0x167f, 0x167f, 0x167f, 0x167f, 0x167f, 0x167f, 0x167f, /* p2r2 */
0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, 0x1a7f, /* p2r3 */
0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, 0x137f, /* p3r0 */
0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, 0x1f7f, /* p3r1 */
0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, 0x177f, /* p3r2 */
0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f, 0x1b7f /* p3r3 */
};
#endif
void comp_fpp_opp(uae_u32 opcode, uae_u16 extra)
{
int reg;
int src;
switch ((extra >> 13) & 0x7)
{
case 1: /* illegal */
break;
case 3: /* FMOVE Fpn,<ea> */
/* 2nd most common */
if (jit_disable.fmove)
{
FAIL(1);
return;
}
if (put_fp_value((extra >> 7) & 7, opcode, extra) < 0)
{
FAIL(1);
return;
}
return;
case 6: /* FMOVEM <ea>,<reglist> */
case 7: /* FMOVEM <reglist>,<ea> */
if (jit_disable.fmovem)
{
FAIL(1);
return;
}
{
int ad;
uae_u32 list = 0;
int incr = 0;
if (extra & 0x2000)
{
/* FMOVEM FPP->memory */
switch ((extra >> 11) & 3)
{ /* Get out early if failure */
case 0: /* static pred */
case 2: /* static postinc */
break;
case 1: /* dynamic pred */
case 3: /* dynamic postinc */
default:
FAIL(1);
return;
}
if ((ad = get_fp_ad(opcode)) < 0)
{
FAIL(1);
return;
}
switch ((extra >> 11) & 3)
{
case 0: /* static pred */
list = extra & 0xff;
incr = -1;
break;
case 2: /* static postinc */
list = extra & 0xff;
incr = 1;
break;
case 1: /* dynamic pred */
case 3: /* dynamic postinc */
abort();
}
if (incr < 0)
{ /* Predecrement */
for (reg = 7; reg >= 0; reg--)
{
if (list & 0x80)
{
fmov_ext_mr((uintptr) temp_fp, reg);
delay;
sub_l_ri(ad, 4);
mov_l_rm(S2, (uintptr) temp_fp);
writelong_clobber(ad, S2, S3);
sub_l_ri(ad, 4);
mov_l_rm(S2, (uintptr) temp_fp + 4);
writelong_clobber(ad, S2, S3);
sub_l_ri(ad, 4);
mov_w_rm(S2, (uintptr) temp_fp + 8);
writeword_clobber(ad, S2, S3);
}
list <<= 1;
}
} else
{ /* Postincrement */
for (reg = 0; reg < 8; reg++)
{
if (list & 0x80)
{
fmov_ext_mr((uintptr) temp_fp, reg);
delay;
mov_w_rm(S2, (uintptr) temp_fp + 8);
writeword_clobber(ad, S2, S3);
add_l_ri(ad, 4);
mov_l_rm(S2, (uintptr) temp_fp + 4);
writelong_clobber(ad, S2, S3);
add_l_ri(ad, 4);
mov_l_rm(S2, (uintptr) temp_fp);
writelong_clobber(ad, S2, S3);
add_l_ri(ad, 4);
}
list <<= 1;
}
}
if ((opcode & 0x38) == 0x18)
mov_l_rr((opcode & 7) + 8, ad);
if ((opcode & 0x38) == 0x20)
mov_l_rr((opcode & 7) + 8, ad);
} else
{
/* FMOVEM memory->FPP */
int ad;
switch ((extra >> 11) & 3)
{ /* Get out early if failure */
case 0: /* static pred */
case 2: /* static postinc */
break;
case 1: /* dynamic pred */
case 3: /* dynamic postinc */
default:
FAIL(1);
return;
}
ad = get_fp_ad(opcode);
if (ad < 0)
{
D(bug("no ad\n"));
FAIL(1);
return;
}
switch ((extra >> 11) & 3)
{
case 0: /* static pred */
list = extra & 0xff;
incr = -1;
break;
case 2: /* static postinc */
list = extra & 0xff;
incr = 1;
break;
case 1: /* dynamic pred */
case 3: /* dynamic postinc */
abort();
}
if (incr < 0)
{
// not reached
for (reg = 7; reg >= 0; reg--)
{
if (list & 0x80)
{
sub_l_ri(ad, 4);
readlong(ad, S2, S3);
mov_l_mr((uintptr) (temp_fp), S2);
sub_l_ri(ad, 4);
readlong(ad, S2, S3);
mov_l_mr((uintptr) (temp_fp) + 4, S2);
sub_l_ri(ad, 4);
readword(ad, S2, S3);
mov_w_mr(((uintptr) temp_fp) + 8, S2);
delay2;
fmov_ext_rm(reg, (uintptr) (temp_fp));
}
list <<= 1;
}
} else
{
for (reg = 0; reg < 8; reg++)
{
if (list & 0x80)
{
readword(ad, S2, S3);
mov_w_mr(((uintptr) temp_fp) + 8, S2);
add_l_ri(ad, 4);
readlong(ad, S2, S3);
mov_l_mr((uintptr) (temp_fp) + 4, S2);
add_l_ri(ad, 4);
readlong(ad, S2, S3);
mov_l_mr((uintptr) (temp_fp), S2);
add_l_ri(ad, 4);
delay2;
fmov_ext_rm(reg, (uintptr) (temp_fp));
}
list <<= 1;
}
}
if ((opcode & 0x38) == 0x18)
mov_l_rr((opcode & 7) + 8, ad);
if ((opcode & 0x38) == 0x20)
mov_l_rr((opcode & 7) + 8, ad);
}
}
return;
case 4: /* FMOVEM <ea>,<control> */
case 5: /* FMOVEM <control>,<ea> */
if (jit_disable.fmovec)
{
FAIL(1);
return;
}
/* rare */
if ((opcode & 0x30) == 0)
{
/* <ea> = Dn or An */
if (extra & 0x2000)
{
if (extra & 0x1000)
{
#if HANDLE_FPCR
mov_l_rm(opcode & 15, (uintptr) & fpu.fpcr.rounding_mode);
or_l_rm(opcode & 15, (uintptr) & fpu.fpcr.rounding_precision);
#else
FAIL(1);
return;
#endif
}
if (extra & 0x0800)
{
FAIL(1);
return;
}
if (extra & 0x0400)
{
/* FPIAR: fixme; we cannot correctly return the address from compiled code */
mov_l_rm(opcode & 15, (uintptr) & fpu.instruction_address);
return;
}
} else
{
// gb-- moved here so that we may FAIL() without generating any code
if (extra & 0x0800)
{
// set_fpsr(m68k_dreg (regs, opcode & 15));
FAIL(1);
return;
}
if (extra & 0x1000)
{
#if HANDLE_FPCR
#if defined(FPU_USE_X86_ROUNDING_MODE) && defined(FPU_USE_X86_ROUNDING_PRECISION)
FAIL(1);
return;
#endif
mov_l_rr(S1, opcode & 15);
mov_l_rr(S2, opcode & 15);
and_l_ri(S1, FPCR_ROUNDING_PRECISION);
and_l_ri(S2, FPCR_ROUNDING_MODE);
mov_l_mr((uintptr) & fpu.fpcr.rounding_precision, S1);
mov_l_mr((uintptr) & fpu.fpcr.rounding_mode, S2);
#else
FAIL(1);
return;
#endif
}
if (extra & 0x0400)
{
/* FPIAR: does that make sense at all? */
mov_l_mr((uintptr) & fpu.instruction_address, opcode & 15);
}
return;
}
} else if ((opcode & 0x3f) == 0x3c)
{
/* <ea> = #imm */
if ((extra & 0x2000) == 0)
{
// gb-- moved here so that we may FAIL() without generating any code
if (extra & 0x0800)
{
FAIL(1);
return;
}
if (extra & 0x1000)
{
comp_get_ilong((m68k_pc_offset += 4) - 4);
#if HANDLE_FPCR
#if defined(FPU_USE_X86_ROUNDING_MODE) && defined(FPU_USE_X86_ROUNDING_PRECISION)
FAIL(1);
return;
#endif
// mov_l_mi((uintptr)&regs.fpcr,val);
mov_l_ri(S1, val);
mov_l_ri(S2, val);
and_l_ri(S1, FPCR_ROUNDING_PRECISION);
and_l_ri(S2, FPCR_ROUNDING_MODE);
mov_l_mr((uintptr) & fpu.fpcr.rounding_precision, S1);
mov_l_mr((uintptr) & fpu.fpcr.rounding_mode, S2);
#else
FAIL(1);
return;
#endif
}
if (extra & 0x0400)
{
uae_u32 val = comp_get_ilong((m68k_pc_offset += 4) - 4);
mov_l_mi((uintptr) & fpu.instruction_address, val);
}
return;
}
FAIL(1);
return;
} else if (extra & 0x2000)
{
FAIL(1);
return;
} else
{
FAIL(1);
return;
}
FAIL(1);
return;
case 0:
case 2: /* Extremely common */
reg = (extra >> 7) & 7;
if ((extra & 0xfc00) == 0x5c00)
{
if (jit_disable.fmovecr)
{
FAIL(1);
return;
}
switch (extra & 0x7f)
{
case 0x00:
fmov_pi(reg);
break;
case 0x0b:
fmov_log10_2(reg);
break;
case 0x0c:
#if defined(USE_LONG_DOUBLE) || defined(USE_QUAD_DOUBLE)
fmov_ext_rm(reg, (uintptr) & const_e);
#else
fmov_rm(reg, (uintptr) & const_e);
#endif
break;
case 0x0d:
fmov_log2_e(reg);
break;
case 0x0e:
#if defined(USE_LONG_DOUBLE) || defined(USE_QUAD_DOUBLE)
fmov_ext_rm(reg, (uintptr) & const_log10_e);
#else
fmov_rm(reg, (uintptr) & const_log10_e);
#endif
break;
case 0x0f:
fmov_0(reg);
break;
case 0x30:
fmov_loge_2(reg);
break;
case 0x31:
#if defined(USE_LONG_DOUBLE) || defined(USE_QUAD_DOUBLE)
fmov_ext_rm(reg, (uintptr) & const_loge_10);
#else
fmov_rm(reg, (uintptr) & const_loge_10);
#endif
break;
case 0x32:
fmov_1(reg);
break;
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
#if defined(USE_LONG_DOUBLE) || defined(USE_QUAD_DOUBLE)
case 0x3c:
case 0x3d:
case 0x3e:
case 0x3f:
fmov_ext_rm(reg, (uintptr) (power10 + (extra & 0x7f) - 0x32));
#else
fmov_rm(reg, (uintptr) (power10 + (extra & 0x7f) - 0x32));
#endif
break;
default:
/* This is not valid, so we fail */
FAIL(1);
return;
}
return;
}
switch (extra & 0x7f)
{
case 0x00: /* FMOVE */
case 0x40: /* FSMOVE: Explicit rounding. This is just a quick fix. Same
* for all other cases that have three choices */
case 0x44: /* FDMOVE */
if (jit_disable.fmove)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fmov_rr(reg, src);
MAKE_FPSR(src);
break;
case 0x01: /* FINT */
if (jit_disable.fint)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x02: /* FSINH */
if (jit_disable.fsinh)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x03: /* FINTRZ */
if (jit_disable.fintrz)
{
FAIL(1);
return;
}
#ifdef USE_X86_FPUCW
/* If we have control over the CW, we can do this */
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
mov_l_ri(S1, 16); /* Switch to "round to zero" mode */
fldcw_m_indexed(S1, (uintptr) x86_fpucw);
frndint_rr(reg, src);
/* restore control word */
mov_l_rm(S1, (uintptr) & regs.fpcr);
and_l_ri(S1, 0x000000f0);
fldcw_m_indexed(S1, (uintptr) x86_fpucw);
MAKE_FPSR(reg);
break;
#endif
FAIL(1);
return;
break;
case 0x04: /* FSQRT */
case 0x41: /* FSSQRT */
case 0x45: /* FDSQRT */
if (jit_disable.fsqrt)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fsqrt_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x06: /* FLOGNP1 */
if (jit_disable.flognp1)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x08: /* FETOXM1 */
if (jit_disable.fetoxm1)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x09: /* FTANH */
if (jit_disable.ftanh)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x0a: /* FATAN */
if (jit_disable.fatan)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x0c: /* FASIN */
if (jit_disable.fasin)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x0d: /* FATANH */
if (jit_disable.fatanh)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x0e: /* FSIN */
if (jit_disable.fsin)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fsin_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x0f: /* FTAN */
if (jit_disable.ftan)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x10: /* FETOX */
if (jit_disable.fetox)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fetox_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x11: /* FTWOTOX */
if (jit_disable.ftwotox)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
ftwotox_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x12: /* FTENTOX */
if (jit_disable.ftentox)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x14: /* FLOGN */
if (jit_disable.flogn)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x15: /* FLOG10 */
if (jit_disable.flog10)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x16: /* FLOG2 */
if (jit_disable.flog2)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
flog2_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x18: /* FABS */
case 0x58: /* FSABS */
case 0x5c: /* FDABS */
if (jit_disable.fabs)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fabs_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x19: /* FCOSH */
if (jit_disable.fcosh)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x1a: /* FNEG */
case 0x5a: /* FSNEG */
case 0x5e: /* FDNEG */
if (jit_disable.fneg)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fneg_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x1c: /* FACOS */
if (jit_disable.facos)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x1d: /* FCOS */
if (jit_disable.fcos)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fcos_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x1e: /* FGETEXP */
if (jit_disable.fgetexp)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x1f: /* FGETMAN */
if (jit_disable.fgetman)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x20: /* FDIV */
case 0x60: /* FSDIV */
case 0x64: /* FDDIV */
if (jit_disable.fdiv)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fdiv_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x21: /* FMOD */
if (jit_disable.fmod)
{
FAIL(1);
return;
}
// FIXME: the quotient byte must be computed
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
frem_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x22: /* FADD */
case 0x62: /* FSADD */
case 0x66: /* FDADD */
if (jit_disable.fadd)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fadd_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x23: /* FMUL */
case 0x63: /* FSMUL */
case 0x67: /* FDMUL */
if (jit_disable.fmul)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fmul_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x24: /* FSGLDIV */
if (jit_disable.fsgldiv)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fdiv_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x25: /* FREM */
if (jit_disable.frem)
{
FAIL(1);
return;
}
// gb-- disabled because the quotient byte must be computed
// otherwise, free rotation in ClarisWorks doesn't work.
FAIL(1);
return;
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
frem1_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x26: /* FSCALE */
if (jit_disable.fscale)
{
FAIL(1);
return;
}
FAIL(1);
return;
break;
case 0x27: /* FSGLMUL */
if (jit_disable.fsglmul)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fmul_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x28: /* FSUB */
case 0x68: /* FSSUB */
case 0x6c: /* FDSUB */
if (jit_disable.fsub)
{
FAIL(1);
return;
}
dont_care_fflags();
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fsub_rr(reg, src);
MAKE_FPSR(reg);
break;
case 0x30: /* FSINCOS */
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
if (jit_disable.fsincos)
{
FAIL(1);
return;
}
FAIL(1);
return;
dont_care_fflags();
break;
case 0x38: /* FCMP */
if (jit_disable.fcmp)
{
FAIL(1);
return;
}
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fmov_rr(FP_RESULT, reg);
fsub_rr(FP_RESULT, src); /* Right way? */
break;
case 0x3a: /* FTST */
if (jit_disable.ftst)
{
FAIL(1);
return;
}
src = get_fp_value(opcode, extra);
if (src < 0)
{
FAIL(1); /* Illegal instruction */
return;
}
fmov_rr(FP_RESULT, src);
break;
default:
FAIL(1);
return;
break;
}
return;
}
FAIL(1);
}
#endif