target: e2k: add basic fpu instructions
This commit is contained in:
parent
c78d3d7521
commit
afce8392d8
|
@ -53,6 +53,7 @@ static void e2k_cpu_reset(DeviceState *dev)
|
|||
env->bn.size = 8;
|
||||
env->bn.cur = 0;
|
||||
env->aau.incrs[0] = 1; /* always one */
|
||||
env->fpcr._one = 1;
|
||||
|
||||
// FIXME: testing
|
||||
env->idr = 0x3a207; // mimic 8c
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "qemu/bswap.h"
|
||||
#include "cpu-qom.h"
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "fpu/softfloat.h"
|
||||
|
||||
void e2k_tcg_initialize(void);
|
||||
|
||||
|
@ -447,6 +448,54 @@ typedef union {
|
|||
uint64_t raw;
|
||||
} E2KCtpr;
|
||||
|
||||
/* E2K FPU regs are compatible with x87 regs */
|
||||
#define FPSR_IE (1U << 0) /* invalid operation */
|
||||
#define FPSR_DE (1U << 1) /* denormalized operand */
|
||||
#define FPSR_ZE (1U << 2) /* zero divide */
|
||||
#define FPSR_OE (1U << 3) /* overflow */
|
||||
#define FPSR_UE (1U << 4) /* underflow */
|
||||
#define FPSR_PE (1U << 5) /* precision */
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t ef : 6; /* exception flags */
|
||||
uint32_t sf : 1; /* stack fault, unused */
|
||||
uint32_t es : 1; /* error summary status */
|
||||
uint32_t _c0 : 1; /* condition code 0, unused */
|
||||
uint32_t c1 : 1; /* condition code 1 */
|
||||
uint32_t _c2 : 1; /* condition code 2, unused */
|
||||
uint32_t top: 3; /* stack top, unused */
|
||||
uint32_t _c3 : 1; /* condition code 3, unused */
|
||||
uint32_t b : 1; /* fpu busy */
|
||||
};
|
||||
uint32_t raw;
|
||||
} E2KFpsrState;
|
||||
|
||||
#define FPCR_EM (FPSR_IE|FPSR_DE|FPSR_ZE|FPSR_OE|FPSR_UE|FPSR_PE)
|
||||
|
||||
#define FPCR_PC_SP 0 /* single precision (32 bits) */
|
||||
#define FPCR_PC_RESERVED 1 /* reserved */
|
||||
#define FPCR_PC_DP 2 /* double precision (64 bits) */
|
||||
#define FPCR_PC_XP 3 /* extended precision (80 bits) */
|
||||
|
||||
#define FPCR_RC_NEAR 0 /* round to nearest */
|
||||
#define FPCR_RC_DOWN 1 /* round down */
|
||||
#define FPCR_RC_UP 2 /* round up */
|
||||
#define FPCR_RC_CHOP 3 /* round toward zero (truncate) */
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t em : 6; /* masks flags */
|
||||
uint32_t _one : 1; /* reserved, always 1 (?) */
|
||||
uint32_t _zero0 : 1; /* reserved, always 0 */
|
||||
uint32_t pc : 2; /* precision control */
|
||||
uint32_t rc : 2; /* rounding control */
|
||||
uint32_t ic : 1; /* infinity control */
|
||||
uint32_t _zero1 : 3; /* reserved */
|
||||
};
|
||||
uint32_t raw;
|
||||
} E2KFpcrState;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
uint64_t lo;
|
||||
|
@ -510,9 +559,11 @@ typedef struct {
|
|||
uint64_t idr;
|
||||
|
||||
uint32_t pfpfr; // Packed Floating Point Flag Register (PFPFR)
|
||||
uint32_t fpcr; // Floating point control register (FPCR)
|
||||
uint32_t fpsr; // Floating point state register (FPSR)
|
||||
E2KFpsrState fpsr; // Floating point state register (FPSR)
|
||||
E2KFpcrState fpcr; // Floating point control register (FPCR)
|
||||
|
||||
float_status fp_status;
|
||||
|
||||
E2KAauState aau;
|
||||
|
||||
int interrupt_index;
|
||||
|
@ -563,6 +614,7 @@ void e2k_break_save_state(CPUE2KState *env);
|
|||
bool e2k_cpu_tlb_fill(CPUState *cpu, vaddr address, int size,
|
||||
MMUAccessType access_type, int mmu_idx,
|
||||
bool probe, uintptr_t retaddr);
|
||||
void e2k_update_fp_status(CPUE2KState *env);
|
||||
|
||||
#define cpu_signal_handler e2k_cpu_signal_handler
|
||||
#define cpu_list e2k_cpu_list
|
||||
|
|
|
@ -27,3 +27,22 @@ DEF_HELPER_2(pmovmskb, i64, i64, i64)
|
|||
DEF_HELPER_1(aau_load_program, void, env)
|
||||
DEF_HELPER_3(mova_ptr, tl, env, int, int)
|
||||
DEF_HELPER_3(aau_am, void, env, int, int)
|
||||
|
||||
#define DEF_HELPER_3_32_64(name) \
|
||||
DEF_HELPER_3(name##s, i32, env, i32, i32) \
|
||||
DEF_HELPER_3(name##d, i64, env, i64, i64)
|
||||
|
||||
DEF_HELPER_3_32_64(fadd)
|
||||
DEF_HELPER_3_32_64(fsub)
|
||||
DEF_HELPER_3_32_64(fmin)
|
||||
DEF_HELPER_3_32_64(fmax)
|
||||
DEF_HELPER_3_32_64(fmul)
|
||||
DEF_HELPER_3_32_64(fdiv)
|
||||
DEF_HELPER_3_32_64(fcmpeq)
|
||||
DEF_HELPER_3_32_64(fcmpneq)
|
||||
DEF_HELPER_3_32_64(fcmple)
|
||||
DEF_HELPER_3_32_64(fcmpnle)
|
||||
DEF_HELPER_3_32_64(fcmplt)
|
||||
DEF_HELPER_3_32_64(fcmpnlt)
|
||||
DEF_HELPER_3_32_64(fcmpuod)
|
||||
DEF_HELPER_3_32_64(fcmpod)
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "exec/helper-proto.h"
|
||||
|
||||
static inline void fpu_set_exception(CPUE2KState *env, int mask)
|
||||
{
|
||||
env->fpsr.ef |= mask;
|
||||
if (env->fpsr.ef & (~env->fpcr.em & FPCR_EM)) {
|
||||
env->fpsr.es = 1;
|
||||
env->fpsr.b = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t save_exception_flags(CPUE2KState *env)
|
||||
{
|
||||
uint8_t old_flags = get_float_exception_flags(&env->fp_status);
|
||||
set_float_exception_flags(0, &env->fp_status);
|
||||
return old_flags;
|
||||
}
|
||||
|
||||
static inline void merge_exception_flags(CPUE2KState *env, uint8_t old_flags)
|
||||
{
|
||||
uint8_t new_flags = get_float_exception_flags(&env->fp_status);
|
||||
float_raise(old_flags, &env->fp_status);
|
||||
fpu_set_exception(env,
|
||||
((new_flags & float_flag_invalid ? FPSR_IE : 0) |
|
||||
(new_flags & float_flag_divbyzero ? FPSR_ZE : 0) |
|
||||
(new_flags & float_flag_overflow ? FPSR_OE : 0) |
|
||||
(new_flags & float_flag_underflow ? FPSR_UE : 0) |
|
||||
(new_flags & float_flag_inexact ? FPSR_PE : 0) |
|
||||
(new_flags & float_flag_input_denormal ? FPSR_DE : 0)));
|
||||
}
|
||||
|
||||
void e2k_update_fp_status(CPUE2KState *env)
|
||||
{
|
||||
int x;
|
||||
|
||||
switch(env->fpcr.rc) {
|
||||
case FPCR_RC_UP:
|
||||
x = float_round_up;
|
||||
break;
|
||||
case FPCR_RC_DOWN:
|
||||
x = float_round_down;
|
||||
break;
|
||||
case FPCR_RC_CHOP:
|
||||
x = float_round_to_zero;
|
||||
break;
|
||||
case FPCR_RC_NEAR:
|
||||
x = float_round_nearest_even;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
set_float_rounding_mode(x, &env->fp_status);
|
||||
|
||||
switch(env->fpcr.pc) {
|
||||
case FPCR_PC_XP: x = 80; break;
|
||||
case FPCR_PC_DP: x = 64; break;
|
||||
case FPCR_PC_SP: x = 32; break;
|
||||
case FPCR_PC_RESERVED:
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
|
||||
set_floatx80_rounding_precision(x, &env->fp_status);
|
||||
|
||||
}
|
||||
|
||||
#define GENERATE_SIMPLE_FLOAT2_OP(name, function, size) \
|
||||
uint##size##_t HELPER(name)(CPUE2KState *env, uint##size##_t x, uint##size##_t y) \
|
||||
{ \
|
||||
uint8_t old_flags = save_exception_flags(env); \
|
||||
float##size z = float##size##_##function (make_float##size (x), make_float##size (y), &env->fp_status); \
|
||||
merge_exception_flags(env, old_flags); \
|
||||
return float##size##_val(z); \
|
||||
}
|
||||
|
||||
#define GENERATE_CMP_FLOAT2_OP(name, function, expr, size) \
|
||||
uint##size##_t HELPER(name)(CPUE2KState *env, uint##size##_t x, uint##size##_t y) \
|
||||
{ \
|
||||
uint8_t old_flags = save_exception_flags(env); \
|
||||
uint##size##_t z = expr float##size##_##function (make_float##size (x), make_float##size (y), &env->fp_status); \
|
||||
merge_exception_flags(env, old_flags); \
|
||||
return z; \
|
||||
}
|
||||
|
||||
#define GENERATE_SIMPLE_FLOAT2_OPS_32_64(name, function) \
|
||||
GENERATE_SIMPLE_FLOAT2_OP(name##s, function, 32) \
|
||||
GENERATE_SIMPLE_FLOAT2_OP(name##d, function, 64)
|
||||
|
||||
#define GENERATE_CMP_FLOAT2_OPS_32_64(name, function, expr) \
|
||||
GENERATE_CMP_FLOAT2_OP(name##s, function, 32) \
|
||||
GENERATE_CMP_FLOAT2_OP(name##d, function, 64)
|
||||
|
||||
|
||||
GENERATE_SIMPLE_FLOAT2_OPS_32_64(fadd, add)
|
||||
GENERATE_SIMPLE_FLOAT2_OPS_32_64(fsub, sub)
|
||||
GENERATE_SIMPLE_FLOAT2_OPS_32_64(fmin, min)
|
||||
GENERATE_SIMPLE_FLOAT2_OPS_32_64(fmax, max)
|
||||
GENERATE_SIMPLE_FLOAT2_OPS_32_64(fmul, mul)
|
||||
GENERATE_SIMPLE_FLOAT2_OPS_32_64(fdiv, div)
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmpeq, eq, )
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmpneq, eq, !)
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmple, le, )
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmpnle, le, !)
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmplt, lt, )
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmpnlt, lt, !)
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmpuod, unordered, )
|
||||
GENERATE_CMP_FLOAT2_OPS_32_64(fcmpod, unordered, !)
|
|
@ -40,6 +40,8 @@ static uint64_t* state_reg_ptr(CPUE2KState *env, int idx)
|
|||
case 0x53: return &env->pregs; /* %cr1.lo */
|
||||
case 0x80: return &env->upsr; /* %upsr */
|
||||
case 0x83: return &env->lsr; /* %lsr */
|
||||
case 0x85: return &env->fpcr.raw; /* %fpcr */
|
||||
case 0x86: return &env->fpsr.raw; /* %fpsr */
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +88,7 @@ uint32_t helper_state_reg_read_i32(CPUE2KState *env, int idx)
|
|||
void helper_state_reg_write_i64(CPUE2KState *env, int idx, uint64_t val)
|
||||
{
|
||||
uint64_t *p = state_reg_ptr(env, idx);
|
||||
|
||||
|
||||
if (p != NULL) {
|
||||
*p = val;
|
||||
} else {
|
||||
|
@ -101,6 +103,10 @@ void helper_state_reg_write_i32(CPUE2KState *env, int idx, uint32_t val)
|
|||
|
||||
if (p != NULL) {
|
||||
*p = val;
|
||||
|
||||
if (idx == 0x85) { /* %fpcr */
|
||||
e2k_update_fp_status(env);
|
||||
}
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP, "unknown state register 0x%x\n", idx);
|
||||
abort();
|
||||
|
|
|
@ -4,6 +4,7 @@ e2k_ss.add(files(
|
|||
'gdbstub.c',
|
||||
'helper.c',
|
||||
'helper_aau.c',
|
||||
'helper_fpu.c',
|
||||
'helper_int.c',
|
||||
'helper_sm.c',
|
||||
'helper_vec.c',
|
||||
|
|
|
@ -1741,6 +1741,19 @@ static void gen_alopf1_i64(DisasContext *ctx, int chan,
|
|||
gen_al_result_i64(ctx, chan, dst, tag);
|
||||
}
|
||||
|
||||
static void gen_alopf1_i64_env(DisasContext *ctx, int chan,
|
||||
void (*op)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64))
|
||||
{
|
||||
Src64 s1 = get_src1_i64(ctx, chan);
|
||||
Src64 s2 = get_src2_i64(ctx, chan);
|
||||
TCGv_i32 tag = e2k_get_temp_i32(ctx);
|
||||
TCGv_i64 dst = e2k_get_temp_i64(ctx);
|
||||
|
||||
gen_tag2_i64(tag, s1.tag, s2.tag);
|
||||
(*op)(dst, cpu_env, s1.value, s2.value);
|
||||
gen_al_result_i64(ctx, chan, dst, tag);
|
||||
}
|
||||
|
||||
static void gen_alopf1_i32(DisasContext *ctx, int chan,
|
||||
void (*op)(TCGv_i32, TCGv_i32, TCGv_i32))
|
||||
{
|
||||
|
@ -1754,6 +1767,19 @@ static void gen_alopf1_i32(DisasContext *ctx, int chan,
|
|||
gen_al_result_i32(ctx, chan, dst, tag);
|
||||
}
|
||||
|
||||
static void gen_alopf1_i32_env(DisasContext *ctx, int chan,
|
||||
void (*op)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32))
|
||||
{
|
||||
Src32 s1 = get_src1_i32(ctx, chan);
|
||||
Src32 s2 = get_src2_i32(ctx, chan);
|
||||
TCGv_i32 tag = e2k_get_temp_i32(ctx);
|
||||
TCGv_i32 dst = e2k_get_temp_i32(ctx);
|
||||
|
||||
gen_tag2_i32(tag, s1.tag, s2.tag);
|
||||
(*op)(dst, cpu_env, s1.value, s2.value);
|
||||
gen_al_result_i32(ctx, chan, dst, tag);
|
||||
}
|
||||
|
||||
static void gen_alopf1_i32_i64(DisasContext *ctx, int chan,
|
||||
void (*op)(TCGv_i64, TCGv_i32, TCGv_i32))
|
||||
{
|
||||
|
@ -2034,6 +2060,76 @@ static void execute_ext_00(DisasContext *ctx, Instr *instr)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 0x30:
|
||||
if (ctx->version >= 4 || is_chan_0134(chan)) {
|
||||
/* faddd */
|
||||
gen_alopf1_i32_env(ctx, chan, gen_helper_fadds);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x31:
|
||||
if (ctx->version >= 4 || is_chan_0134(chan)) {
|
||||
/* faddd */
|
||||
gen_alopf1_i64_env(ctx, chan, gen_helper_faddd);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x32:
|
||||
if (ctx->version >= 4 || is_chan_0134(chan)) {
|
||||
/* fsubs */
|
||||
gen_alopf1_i32_env(ctx, chan, gen_helper_fsubs);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x33:
|
||||
if (ctx->version >= 4 || is_chan_0134(chan)) {
|
||||
/* fsubd */
|
||||
gen_alopf1_i64_env(ctx, chan, gen_helper_fsubd);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x34:
|
||||
if (is_chan_0134(chan)) {
|
||||
/* fmins */
|
||||
gen_alopf1_i32_env(ctx, chan, gen_helper_fmins);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x35:
|
||||
if (is_chan_0134(chan)) {
|
||||
/* fmind */
|
||||
gen_alopf1_i64_env(ctx, chan, gen_helper_fmind);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x36:
|
||||
if (is_chan_0134(chan)) {
|
||||
/* fmaxs */
|
||||
gen_alopf1_i32_env(ctx, chan, gen_helper_fmaxs);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x37:
|
||||
if (is_chan_0134(chan)) {
|
||||
/* fmaxd */
|
||||
gen_alopf1_i64_env(ctx, chan, gen_helper_fmaxd);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x38:
|
||||
if (ctx->version >= 4 || is_chan_0134(chan)) {
|
||||
/* fmuls */
|
||||
gen_alopf1_i32_env(ctx, chan, gen_helper_fmuls);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x39:
|
||||
if (is_chan_0134(chan)) {
|
||||
/* fmuld */
|
||||
gen_alopf1_i64_env(ctx, chan, gen_helper_fmuld);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
if (chan == 5) {
|
||||
// FIXME: temp hack
|
||||
|
@ -2068,6 +2164,20 @@ static void execute_ext_00(DisasContext *ctx, Instr *instr)
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case 0x48:
|
||||
if (chan == 5) {
|
||||
/* fdivs */
|
||||
gen_alopf1_i32_env(ctx, chan, gen_helper_fdivs);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x49:
|
||||
if (chan == 5) {
|
||||
/* fdivd */
|
||||
gen_alopf1_i64_env(ctx, chan, gen_helper_fdivd);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x61:
|
||||
if (is_chan_0134(chan)) {
|
||||
gen_movtd(ctx, chan);
|
||||
|
|
Loading…
Reference in New Issue