Commit fb0eaffc authored by bellard's avatar bellard
Browse files

PowerPC fixes (Jocelyn Mayer)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@483 c046a42c-6fe2-441c-8c8c-71466251a162
parent 07ad1b93
......@@ -152,7 +152,7 @@ typedef struct CPUPPCState {
/* general purpose registers */
uint32_t gpr[32];
/* floating point registers */
uint64_t fpr[32];
double fpr[32];
/* segment registers */
ppc_sr_t sr[16];
/* special purpose registers */
......@@ -172,7 +172,10 @@ typedef struct CPUPPCState {
uint32_t exception;
/* qemu dedicated */
uint64_t ft0; /* temporary float register */
/* temporary float registers */
double ft0;
double ft1;
double ft2;
int interrupt_request;
jmp_buf jmp_env;
int exception_index;
......@@ -374,35 +377,4 @@ enum {
EXCP_BRANCH = 0x104, /* branch instruction */
};
/*
* We need to put in some extra aux table entries to tell glibc what
* the cache block size is, so it can use the dcbz instruction safely.
*/
#define AT_DCACHEBSIZE 19
#define AT_ICACHEBSIZE 20
#define AT_UCACHEBSIZE 21
/* A special ignored type value for PPC, for glibc compatibility. */
#define AT_IGNOREPPC 22
/*
* The requirements here are:
* - keep the final alignment of sp (sp & 0xf)
* - make sure the 32-bit value at the first 16 byte aligned position of
* AUXV is greater than 16 for glibc compatibility.
* AT_IGNOREPPC is used for that.
* - for compatibility with glibc ARCH_DLINFO must always be defined on PPC,
* even if DLINFO_ARCH_ITEMS goes to zero or is undefined.
*/
#define DLINFO_ARCH_ITEMS 3
#define ARCH_DLINFO \
do { \
/* \
* Now handle glibc compatibility. \
*/ \
NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
\
NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \
NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \
NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
} while (0)
#endif /* !defined (__CPU_PPC_H__) */
......@@ -29,6 +29,12 @@ register uint32_t T2 asm(AREG3);
#define PARAM(n) ((uint32_t)PARAM##n)
#define SPARAM(n) ((int32_t)PARAM##n)
#define FT0 (env->ft0)
#define FT1 (env->ft1)
#define FT2 (env->ft2)
#define FTS0 ((float)env->ft0)
#define FTS1 ((float)env->ft1)
#define FTS2 ((float)env->ft2)
#define RETURN() __asm__ __volatile__("");
......@@ -145,8 +151,8 @@ uint32_t do_load_xer (void);
void do_store_xer (uint32_t value);
uint32_t do_load_msr (void);
void do_store_msr (uint32_t msr_value);
uint32_t do_load_fpscr (void);
void do_store_fpscr (uint8_t mask, uint32_t fp);
void do_load_fpscr (void);
void do_store_fpscr (uint32_t mask);
int32_t do_sraw(int32_t Ta, uint32_t Tb);
void do_lmw (int reg, uint32_t src);
......@@ -154,4 +160,7 @@ void do_stmw (int reg, uint32_t dest);
void do_lsw (uint32_t reg, int count, uint32_t src);
void do_stsw (uint32_t reg, int count, uint32_t dest);
void do_dcbz (void);
void do_icbi (void);
#endif /* !defined (__PPC_H__) */
......@@ -121,67 +121,67 @@ void do_store_msr (uint32_t msr_value)
}
/* The 32 MSB of the target fpr are undefined. They'll be zero... */
uint32_t do_load_fpscr (void)
/* Floating point operations helpers */
void do_load_fpscr (void)
{
return (fpscr_fx << FPSCR_FX) |
(fpscr_fex << FPSCR_FEX) |
(fpscr_vx << FPSCR_VX) |
(fpscr_ox << FPSCR_OX) |
(fpscr_ux << FPSCR_UX) |
(fpscr_zx << FPSCR_ZX) |
(fpscr_xx << FPSCR_XX) |
(fpscr_vsxnan << FPSCR_VXSNAN) |
(fpscr_vxisi << FPSCR_VXISI) |
(fpscr_vxidi << FPSCR_VXIDI) |
(fpscr_vxzdz << FPSCR_VXZDZ) |
(fpscr_vximz << FPSCR_VXIMZ) |
(fpscr_fr << FPSCR_FR) |
(fpscr_fi << FPSCR_FI) |
(fpscr_fprf << FPSCR_FPRF) |
(fpscr_vxsoft << FPSCR_VXSOFT) |
(fpscr_vxsqrt << FPSCR_VXSQRT) |
(fpscr_oe << FPSCR_OE) |
(fpscr_ue << FPSCR_UE) |
(fpscr_ze << FPSCR_ZE) |
(fpscr_xe << FPSCR_XE) |
(fpscr_ni << FPSCR_NI) |
(fpscr_rn << FPSCR_RN);
/* The 32 MSB of the target fpr are undefined.
* They'll be zero...
*/
union {
double d;
struct {
uint32_t u[2];
} s;
} u;
int i;
u.s.u[0] = 0;
u.s.u[1] = 0;
for (i = 0; i < 8; i++)
u.s.u[1] |= env->fpscr[i] << (4 * i);
FT0 = u.d;
}
/* We keep only 32 bits of input... */
/* For now, this is COMPLETELY BUGGY ! */
void do_store_fpscr (uint8_t mask, uint32_t fp)
void do_store_fpscr (uint32_t mask)
{
/*
* We use only the 32 LSB of the incoming fpr
*/
union {
double d;
struct {
uint32_t u[2];
} s;
} u;
int i;
for (i = 0; i < 7; i++) {
if ((mask & (1 << i)) == 0)
fp &= ~(0xf << (4 * i));
u.d = FT0;
if (mask & 0x80)
env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[1] >> 28) & ~0x9);
for (i = 1; i < 7; i++) {
if (mask & (1 << (7 - i)))
env->fpscr[i] = (u.s.u[1] >> (4 * (7 - i))) & 0xF;
}
/* TODO: update FEX & VX */
/* Set rounding mode */
switch (env->fpscr[0] & 0x3) {
case 0:
/* Best approximation (round to nearest) */
fesetround(FE_TONEAREST);
break;
case 1:
/* Smaller magnitude (round toward zero) */
fesetround(FE_TOWARDZERO);
break;
case 2:
/* Round toward +infinite */
fesetround(FE_UPWARD);
break;
case 3:
/* Round toward -infinite */
fesetround(FE_DOWNWARD);
break;
}
if ((mask & 80) != 0)
fpscr_fx = (fp >> FPSCR_FX) & 0x01;
fpscr_fex = (fp >> FPSCR_FEX) & 0x01;
fpscr_vx = (fp >> FPSCR_VX) & 0x01;
fpscr_ox = (fp >> FPSCR_OX) & 0x01;
fpscr_ux = (fp >> FPSCR_UX) & 0x01;
fpscr_zx = (fp >> FPSCR_ZX) & 0x01;
fpscr_xx = (fp >> FPSCR_XX) & 0x01;
fpscr_vsxnan = (fp >> FPSCR_VXSNAN) & 0x01;
fpscr_vxisi = (fp >> FPSCR_VXISI) & 0x01;
fpscr_vxidi = (fp >> FPSCR_VXIDI) & 0x01;
fpscr_vxzdz = (fp >> FPSCR_VXZDZ) & 0x01;
fpscr_vximz = (fp >> FPSCR_VXIMZ) & 0x01;
fpscr_fr = (fp >> FPSCR_FR) & 0x01;
fpscr_fi = (fp >> FPSCR_FI) & 0x01;
fpscr_fprf = (fp >> FPSCR_FPRF) & 0x1F;
fpscr_vxsoft = (fp >> FPSCR_VXSOFT) & 0x01;
fpscr_vxsqrt = (fp >> FPSCR_VXSQRT) & 0x01;
fpscr_oe = (fp >> FPSCR_OE) & 0x01;
fpscr_ue = (fp >> FPSCR_UE) & 0x01;
fpscr_ze = (fp >> FPSCR_ZE) & 0x01;
fpscr_xe = (fp >> FPSCR_XE) & 0x01;
fpscr_ni = (fp >> FPSCR_NI) & 0x01;
fpscr_rn = (fp >> FPSCR_RN) & 0x03;
}
int32_t do_sraw(int32_t value, uint32_t shift)
......@@ -220,20 +220,14 @@ void do_lsw (uint32_t reg, int count, uint32_t src)
int sh;
for (; count > 3; count -= 4, src += 4) {
if (reg == 32)
reg = 0;
ugpr(reg++) = ld32(src);
if (T2 == 32)
T2 = 0;
}
if (count > 0) {
for (sh = 24, tmp = 0; count > 0; count--, src++, sh -= 8) {
if (reg == 32)
reg = 0;
tmp |= ld8(src) << sh;
if (sh == 0) {
sh = 32;
ugpr(reg++) = tmp;
tmp = 0;
}
for (sh = 24; count > 0; count--, src++, sh -= 8) {
tmp |= ld8(src) << sh;
}
ugpr(reg) = tmp;
}
......@@ -244,19 +238,30 @@ void do_stsw (uint32_t reg, int count, uint32_t dest)
int sh;
for (; count > 3; count -= 4, dest += 4) {
st32(dest, ugpr(reg++));
if (reg == 32)
reg = 0;
st32(dest, ugpr(reg++));
}
if (count > 0) {
for (sh = 24; count > 0; count--, dest++, sh -= 8) {
if (reg == 32)
reg = 0;
st8(dest, (ugpr(reg) >> sh) & 0xFF);
if (sh == 0) {
sh = 32;
reg++;
}
}
}
void do_dcbz (void)
{
int i;
/* Assume cache line size is 32 */
for (i = 0; i < 8; i++) {
st32(T0, 0);
T0 += 4;
}
}
/* Instruction cache invalidation helper */
void do_icbi (void)
{
tb_invalidate_page(T0);
}
......@@ -27,6 +27,12 @@
#define Ts2 (int32_t)T2
#define FT0 (env->ft0)
#define FT1 (env->ft1)
#define FT2 (env->ft2)
#define FTS0 ((float)env->ft0)
#define FTS1 ((float)env->ft1)
#define FTS2 ((float)env->ft2)
#define PPC_OP(name) void op_##name(void)
......@@ -173,6 +179,13 @@ PPC_OP(set_Rc0_1)
RETURN();
}
/* Set Rc1 (for floating point arithmetic) */
PPC_OP(set_Rc1)
{
env->crf[1] = regs->fpscr[7];
RETURN();
}
PPC_OP(set_T0)
{
T0 = PARAM(1);
......@@ -278,6 +291,25 @@ PPC_OP(load_lr)
RETURN();
}
/* FPSCR */
PPC_OP(load_fpscr)
{
do_load_fpscr();
RETURN();
}
PPC_OP(store_fpscr)
{
do_store_fpscr(PARAM(1));
RETURN();
}
PPC_OP(reset_scrfx)
{
regs->fpscr[7] &= ~0x8;
RETURN();
}
/* Set reservation */
PPC_OP(set_reservation)
{
......@@ -988,7 +1020,7 @@ PPC_OP(xori)
/* rotate left word immediate then mask insert */
PPC_OP(rlwimi)
{
T0 = rotl(T0, PARAM(1) & PARAM(2)) | (T0 & PARAM(3));
T0 = (rotl(T0, PARAM(1)) & PARAM(2)) | (T1 & PARAM(3));
RETURN();
}
......@@ -1216,123 +1248,171 @@ PPC_OP(store_spr)
regs->spr[PARAM(1)] = T0;
}
/* FPSCR */
PPC_OP(load_fpscr)
{
T0 = do_load_fpscr();
}
PPC_OP(store_fpscr)
{
do_store_fpscr(PARAM(1), T0);
}
/*** Floating-point store ***/
static inline uint32_t dtos(uint64_t f)
{
unsigned int e, m, s;
e = (((f >> 52) & 0x7ff) - 1022) + 126;
s = (f >> 63);
m = (f >> 29);
return (s << 31) | (e << 23) | m;
}
static inline uint64_t stod(uint32_t f)
{
unsigned int e, m, s;
e = ((f >> 23) & 0xff) - 126 + 1022;
s = f >> 31;
m = f & ((1 << 23) - 1);
return ((uint64_t)s << 63) | ((uint64_t)e << 52) | ((uint64_t)m << 29);
}
PPC_OP(stfd_z_FT0)
{
st64(SPARAM(1), FT0);
stfq((void *)SPARAM(1), FT0);
}
PPC_OP(stfd_FT0)
{
T0 += SPARAM(1);
st64(T0, FT0);
stfq((void *)T0, FT0);
}
PPC_OP(stfdx_z_FT0)
{
st64(T0, FT0);
stfq((void *)T0, FT0);
}
PPC_OP(stfdx_FT0)
{
T0 += T1;
st64(T0, FT0);
stfq((void *)T0, FT0);
}
PPC_OP(stfs_z_FT0)
{
st32(SPARAM(1), dtos(FT0));
float tmp = FT0;
stfl((void *)SPARAM(1), tmp);
}
PPC_OP(stfs_FT0)
{
float tmp = FT0;
T0 += SPARAM(1);
st32(T0, dtos(FT0));
stfl((void *)T0, tmp);
}
PPC_OP(stfsx_z_FT0)
{
st32(T0, dtos(FT0));
float tmp = FT0;
stfl((void *)T0, tmp);
}
PPC_OP(stfsx_FT0)
{
float tmp = FT0;
T0 += T1;
st32(T0, dtos(FT0));
stfl((void *)T0, tmp);
}
/*** Floating-point load ***/
PPC_OP(lfd_z_FT0)
{
FT0 = ld64(SPARAM(1));
FT0 = ldfq((void *)SPARAM(1));
}
PPC_OP(lfd_FT0)
{
T0 += SPARAM(1);
FT0 = ld64(T0);
FT0 = ldfq((void *)T0);
}
PPC_OP(lfdx_z_FT0)
{
FT0 = ld64(T0);
FT0 = ldfq((void *)T0);
}
PPC_OP(lfdx_FT0)
{
T0 += T1;
FT0 = ld64(T0);
FT0 = ldfq((void *)T0);
}
PPC_OP(lfs_z_FT0)
{
FT0 = stod(ld32(SPARAM(1)));
float tmp = ldfl((void *)SPARAM(1));
FT0 = tmp;
}
PPC_OP(lfs_FT0)
{
float tmp;
T0 += SPARAM(1);
FT0 = stod(ld32(T0));
tmp = ldfl((void *)T0);
FT0 = tmp;
}
PPC_OP(lfsx_z_FT0)
{
FT0 = stod(ld32(T0));
float tmp;
tmp = ldfl((void *)T0);
FT0 = tmp;
}
PPC_OP(lfsx_FT0)
{
float tmp;
T0 += T1;
tmp = ldfl((void *)T0);
FT0 = tmp;
}
PPC_OP(lwarx_z)
{
T1 = ld32(T0);
regs->reserve = T0;
RETURN();
}
PPC_OP(lwarx)
{
T0 += T1;
T1 = ld32(T0);
regs->reserve = T0;
RETURN();
}
PPC_OP(stwcx_z)
{
if (regs->reserve != T0) {
env->crf[0] = xer_ov;
} else {
st32(T0, T1);
env->crf[0] = xer_ov | 0x02;
}
regs->reserve = 0;
RETURN();
}
PPC_OP(stwcx)
{
T0 += T1;
FT0 = stod(ld32(T0));
if (regs->reserve != (T0 & ~0x03)) {
env->crf[0] = xer_ov;
} else {
st32(T0, T2);
env->crf[0] = xer_ov | 0x02;
}
regs->reserve = 0;
RETURN();
}
PPC_OP(dcbz_z)
{
do_dcbz();
RETURN();
}
PPC_OP(dcbz)
{
T0 += T1;
do_dcbz();
RETURN();
}
/* Instruction cache block invalidate */
PPC_OP(icbi_z)
{
do_icbi();
RETURN();
}
PPC_OP(icbi)
{
T0 += T1;
do_icbi();
RETURN();
}
......@@ -70,18 +70,90 @@ void OPPROTO glue(op_store_T1_crf_crf, REG)(void)
regs->crf[REG] = T1;
}
/* Floating point condition and status register moves */
void OPPROTO glue(op_load_fpscr_T0_fpscr, REG)(void)
{
T0 = regs->fpscr[REG];
RETURN();
}
#if REG == 0
void OPPROTO glue(op_store_T0_fpscr_fpscr, REG)(void)
{
regs->fpscr[REG] = (regs->fpscr[REG] & 0x9) | (T0 & ~0x9);
RETURN();
}
void OPPROTO glue(op_store_T0_fpscri_fpscr, REG)(void)
{
regs->fpscr[REG] = (regs->fpscr[REG] & ~0x9) | (PARAM(1) & 0x9);
RETURN();
}
void OPPROTO glue(op_clear_fpscr_fpscr, REG)(void)
{
regs->fpscr[REG] = (regs->fpscr[REG] & 0x9);
RETURN();
}
#else
void OPPROTO glue(op_store_T0_fpscr_fpscr, REG)(void)
{
regs->fpscr[REG] = T0;
RETURN();
}
void OPPROTO glue(op_store_T0_fpscri_fpscr, REG)(void)
{
regs->fpscr[REG] = PARAM(1);
RETURN();
}
void OPPROTO glue(op_clear_fpscr_fpscr, REG)(void)
{
regs->fpscr[REG] = 0x0;
RETURN();
}
#endif
#endif /* REG <= 7 */
/* float moves */
void OPPROTO glue(op_load_FT0_fpr, REG)(void)
/* floating point registers moves */
void OPPROTO glue(op_load_fpr_FT0_fpr, REG)(void)
{
FT0 = env->fpr[REG];
RETURN();
}
void OPPROTO glue(op_store_FT0_fpr, REG)(void)
void OPPROTO glue(op_store_FT0_fpr_fpr, REG)(void)
{
env->fpr[REG] = FT0;
RETURN();
}
void OPPROTO glue(op_load_fpr_FT1_fpr, REG)(void)
{
FT1 = env->fpr[REG];
RETURN();
}
void OPPROTO glue(op_store_FT1_fpr_fpr, REG)(void)
{
env->fpr[REG] = FT1;