--- trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:19:16 19 +++ trunk/src/cpus/cpu_mips_coproc.c 2007/10/08 16:19:23 20 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_mips_coproc.c,v 1.3 2005/10/26 14:37:03 debug Exp $ + * $Id: cpu_mips_coproc.c,v 1.8 2005/11/23 02:17:41 debug Exp $ * * Emulation of MIPS coprocessors. */ @@ -40,6 +40,7 @@ #include "cpu.h" #include "cpu_mips.h" #include "emul.h" +#include "float_emul.h" #include "machine.h" #include "memory.h" #include "mips_cpu_types.h" @@ -414,11 +415,7 @@ if (coproc_nr == 0) { c->nr_of_tlbs = cpu->cd.mips.cpu_type.nr_of_tlb_entries; - c->tlbs = malloc(c->nr_of_tlbs * sizeof(struct mips_tlb)); - if (c->tlbs == NULL) { - fprintf(stderr, "mips_coproc_new(): out of memory\n"); - exit(1); - } + c->tlbs = zeroed_alloc(c->nr_of_tlbs * sizeof(struct mips_tlb)); /* * Start with nothing in the status register. This makes sure @@ -798,13 +795,14 @@ int psize = 12; int or_pmask = 0x1fff; int phys_shift = 12; + int tmp_pmask = cpu->cd.mips.coproc[0]-> + tlbs[i].mask | or_pmask; if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { or_pmask = 0x7ff; phys_shift = 10; } - switch (cpu->cd.mips.coproc[0]-> - tlbs[i].mask | or_pmask) { + switch (tmp_pmask) { case 0x000007ff: psize = 10; break; case 0x00001fff: psize = 12; break; case 0x00007fff: psize = 14; break; @@ -816,7 +814,8 @@ case 0x07ffffff: psize = 26; break; default: printf("invalidate_translation_caches" - "_paddr(): bad pagemask?\n"); + "_paddr(): bad pagemask: 0x%x\n", + (int)tmp_pmask); } tlb_paddr0 = (cpu->cd.mips.coproc[0]->tlbs[i]. lo0 & ENTRYLO_PFN_MASK)>>ENTRYLO_PFN_SHIFT; @@ -1017,6 +1016,7 @@ if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { +#if 0 /* * This speeds up delay-loops that just read the count * register until it has reached a certain value. (Only for @@ -1037,7 +1037,7 @@ cp->reg[COP0_COUNT] = (int64_t) (int32_t)(cp->reg[COP0_COUNT] + increase); } - +#endif unimpl = 0; } if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; @@ -1426,6 +1426,14 @@ * * TODO: Move this to some other file? */ +static int mips_fmt_to_ieee_fmt[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + IEEE_FMT_S, IEEE_FMT_D, 0, 0, + IEEE_FMT_W, IEEE_FMT_L, /* PS (Paired Single) */ 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* MIPS floating point types: */ #define FMT_S 16 #define FMT_D 17 #define FMT_W 20 @@ -1442,150 +1450,7 @@ #define FPU_OP_C 8 #define FPU_OP_ABS 9 #define FPU_OP_NEG 10 -/* TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W, - RSQRT */ - - -struct internal_float_value { - double f; - int nan; -}; - - -/* - * fpu_interpret_float_value(): - * - * Interprets a float value from binary IEEE format into an - * internal_float_value struct. - */ -static void fpu_interpret_float_value(uint64_t reg, - struct internal_float_value *fvp, int fmt) -{ - int n_frac = 0, n_exp = 0; - int i, nan, sign = 0, exponent; - double fraction; - - memset(fvp, 0, sizeof(struct internal_float_value)); - - /* n_frac and n_exp: */ - switch (fmt) { - case FMT_S: n_frac = 23; n_exp = 8; break; - case FMT_W: n_frac = 31; n_exp = 0; break; - case FMT_D: n_frac = 52; n_exp = 11; break; - case FMT_L: n_frac = 63; n_exp = 0; break; - default: - fatal("fpu_interpret_float_value(): " - "unimplemented format %i\n", fmt); - } - - /* exponent: */ - exponent = 0; - switch (fmt) { - case FMT_W: - reg &= 0xffffffffULL; - case FMT_L: - break; - case FMT_S: - reg &= 0xffffffffULL; - case FMT_D: - exponent = (reg >> n_frac) & ((1 << n_exp) - 1); - exponent -= (1 << (n_exp-1)) - 1; - break; - default: - fatal("fpu_interpret_float_value(): unimplemented " - "format %i\n", fmt); - } - - /* nan: */ - nan = 0; - switch (fmt) { - case FMT_S: - if (reg == 0x7fffffffULL || reg == 0x7fbfffffULL) - nan = 1; - break; - case FMT_D: - if (reg == 0x7fffffffffffffffULL || - reg == 0x7ff7ffffffffffffULL) - nan = 1; - break; - } - - if (nan) { - fvp->f = 1.0; - goto no_reasonable_result; - } - - /* fraction: */ - fraction = 0.0; - switch (fmt) { - case FMT_W: - { - int32_t r_int = reg; - fraction = r_int; - } - break; - case FMT_L: - { - int64_t r_int = reg; - fraction = r_int; - } - break; - case FMT_S: - case FMT_D: - /* sign: */ - sign = (reg >> 31) & 1; - if (fmt == FMT_D) - sign = (reg >> 63) & 1; - - fraction = 0.0; - for (i=0; i> i) & 1; - fraction /= 2.0; - if (bit) - fraction += 1.0; - } - /* Add implicit bit 0: */ - fraction = (fraction / 2.0) + 1.0; - break; - default: - fatal("fpu_interpret_float_value(): " - "unimplemented format %i\n", fmt); - } - - /* form the value: */ - fvp->f = fraction; - - /* fatal("load reg=%016llx sign=%i exponent=%i fraction=%f ", - (long long)reg, sign, exponent, fraction); */ - - /* TODO: this is awful for exponents of large magnitude. */ - if (exponent > 0) { - /* - * NOTE / TODO: - * - * This is an ulgy workaround on Alpha, where it seems that - * multiplying by 2, 1024 times causes a floating point - * exception. (Triggered by running for example NetBSD/pmax - * 2.0 on an Alpha.) - */ - if (exponent == 1024) - exponent = 1023; - - while (exponent-- > 0) - fvp->f *= 2.0; - } else if (exponent < 0) { - while (exponent++ < 0) - fvp->f /= 2.0; - } - - if (sign) - fvp->f = -fvp->f; - -no_reasonable_result: - fvp->nan = nan; - - /* fatal("f = %f\n", fvp->f); */ -} +/* TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W, RSQRT */ /* @@ -1596,115 +1461,12 @@ static void fpu_store_float_value(struct mips_coproc *cp, int fd, double nf, int fmt, int nan) { - int n_frac = 0, n_exp = 0, signofs=0; - int i, exponent; - uint64_t r = 0, r2; - int64_t r3; - - /* n_frac and n_exp: */ - switch (fmt) { - case FMT_S: n_frac = 23; n_exp = 8; signofs = 31; break; - case FMT_W: n_frac = 31; n_exp = 0; signofs = 31; break; - case FMT_D: n_frac = 52; n_exp = 11; signofs = 63; break; - case FMT_L: n_frac = 63; n_exp = 0; signofs = 63; break; - default: - fatal("fpu_store_float_value(): unimplemented format" - " %i\n", fmt); - } - - if ((fmt == FMT_S || fmt == FMT_D) && nan) - goto store_nan; - - /* fraction: */ - switch (fmt) { - case FMT_W: - case FMT_L: - /* - * This causes an implicit conversion of double to integer. - * If nf < 0.0, then r2 will begin with a sequence of binary - * 1's, which is ok. - */ - r3 = nf; - r2 = r3; - r |= r2; - - if (fmt == FMT_W) - r &= 0xffffffffULL; - break; - case FMT_S: - case FMT_D: - /* fatal("store f=%f ", nf); */ - - /* sign bit: */ - if (nf < 0.0) { - r |= ((uint64_t)1 << signofs); - nf = -nf; - } - - /* - * How to convert back from double to exponent + fraction: - * We want fraction to be 1.xxx, that is - * 1.0 <= fraction < 2.0 - * - * This method is very slow but should work: - */ - exponent = 0; - while (nf < 1.0 && exponent > -1023) { - nf *= 2.0; - exponent --; - } - while (nf >= 2.0 && exponent < 1023) { - nf /= 2.0; - exponent ++; - } - - /* Here: 1.0 <= nf < 2.0 */ - /* fatal(" nf=%f", nf); */ - nf -= 1.0; /* remove implicit first bit */ - for (i=n_frac-1; i>=0; i--) { - nf *= 2.0; - if (nf >= 1.0) { - r |= ((uint64_t)1 << i); - nf -= 1.0; - } - /* printf("\n i=%2i r=%016llx\n", i, (long long)r); */ - } - - /* Insert the exponent into the resulting word: */ - /* (First bias, then make sure it's within range) */ - exponent += (((uint64_t)1 << (n_exp-1)) - 1); - if (exponent < 0) - exponent = 0; - if (exponent >= ((int64_t)1 << n_exp)) - exponent = ((int64_t)1 << n_exp) - 1; - r |= (uint64_t)exponent << n_frac; - - /* Special case for 0.0: */ - if (exponent == 0) - r = 0; - - /* fatal(" exp=%i, r = %016llx\n", exponent, (long long)r); */ - - break; - default: - /* TODO */ - fatal("fpu_store_float_value(): unimplemented format " - "%i\n", fmt); - } - -store_nan: - if (nan) { - if (fmt == FMT_S) - r = 0x7fffffffULL; - else if (fmt == FMT_D) - r = 0x7fffffffffffffffULL; - else - r = 0x7fffffffULL; - } + int ieee_fmt = mips_fmt_to_ieee_fmt[fmt]; + uint64_t r = ieee_store_float_value(nf, ieee_fmt, nan); /* - * TODO: this is for 32-bit mode. It has to be updated later - * for 64-bit coprocessor stuff. + * TODO: This is for 32-bit mode. It has to be updated later + * for 64-bit coprocessor functionality! */ if (fmt == FMT_D || fmt == FMT_L) { cp->reg[fd] = r & 0xffffffffULL; @@ -1726,19 +1488,18 @@ /* * fpu_op(): * - * Perform a floating-point operation. For those of fs and ft - * that are >= 0, those numbers are interpreted into local - * variables. + * Perform a floating-point operation. For those of fs and ft that are >= 0, + * those numbers are interpreted into local variables. * - * Only FPU_OP_C (compare) returns anything of interest, 1 for - * true, 0 for false. + * Only FPU_OP_C (compare) returns anything of interest, 1 for true, 0 for + * false. */ static int fpu_op(struct cpu *cpu, struct mips_coproc *cp, int op, int fmt, int ft, int fs, int fd, int cond, int output_fmt) { /* Potentially two input registers, fs and ft */ - struct internal_float_value float_value[2]; - int unordered, nan; + struct ieee_float_value float_value[2]; + int unordered, nan, ieee_fmt = mips_fmt_to_ieee_fmt[fmt]; uint64_t fs_v = 0; double nf; @@ -1749,7 +1510,7 @@ if (fmt == FMT_D || fmt == FMT_L) fs_v = (fs_v & 0xffffffffULL) + (cp->reg[(fs + 1) & 31] << 32); - fpu_interpret_float_value(fs_v, &float_value[0], fmt); + ieee_interpret_float_value(fs_v, &float_value[0], ieee_fmt); } if (ft >= 0) { uint64_t v = cp->reg[ft]; @@ -1758,7 +1519,7 @@ if (fmt == FMT_D || fmt == FMT_L) v = (v & 0xffffffffULL) + (cp->reg[(ft + 1) & 31] << 32); - fpu_interpret_float_value(v, &float_value[1], fmt); + ieee_interpret_float_value(v, &float_value[1], ieee_fmt); } switch (op) {