25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $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 $ |
29 |
* |
* |
30 |
* Emulation of MIPS coprocessors. |
* Emulation of MIPS coprocessors. |
31 |
*/ |
*/ |
40 |
#include "cpu.h" |
#include "cpu.h" |
41 |
#include "cpu_mips.h" |
#include "cpu_mips.h" |
42 |
#include "emul.h" |
#include "emul.h" |
43 |
|
#include "float_emul.h" |
44 |
#include "machine.h" |
#include "machine.h" |
45 |
#include "memory.h" |
#include "memory.h" |
46 |
#include "mips_cpu_types.h" |
#include "mips_cpu_types.h" |
415 |
|
|
416 |
if (coproc_nr == 0) { |
if (coproc_nr == 0) { |
417 |
c->nr_of_tlbs = cpu->cd.mips.cpu_type.nr_of_tlb_entries; |
c->nr_of_tlbs = cpu->cd.mips.cpu_type.nr_of_tlb_entries; |
418 |
c->tlbs = malloc(c->nr_of_tlbs * sizeof(struct mips_tlb)); |
c->tlbs = zeroed_alloc(c->nr_of_tlbs * sizeof(struct mips_tlb)); |
|
if (c->tlbs == NULL) { |
|
|
fprintf(stderr, "mips_coproc_new(): out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
419 |
|
|
420 |
/* |
/* |
421 |
* Start with nothing in the status register. This makes sure |
* Start with nothing in the status register. This makes sure |
795 |
int psize = 12; |
int psize = 12; |
796 |
int or_pmask = 0x1fff; |
int or_pmask = 0x1fff; |
797 |
int phys_shift = 12; |
int phys_shift = 12; |
798 |
|
int tmp_pmask = cpu->cd.mips.coproc[0]-> |
799 |
|
tlbs[i].mask | or_pmask; |
800 |
|
|
801 |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
if (cpu->cd.mips.cpu_type.rev == MIPS_R4100) { |
802 |
or_pmask = 0x7ff; |
or_pmask = 0x7ff; |
803 |
phys_shift = 10; |
phys_shift = 10; |
804 |
} |
} |
805 |
switch (cpu->cd.mips.coproc[0]-> |
switch (tmp_pmask) { |
|
tlbs[i].mask | or_pmask) { |
|
806 |
case 0x000007ff: psize = 10; break; |
case 0x000007ff: psize = 10; break; |
807 |
case 0x00001fff: psize = 12; break; |
case 0x00001fff: psize = 12; break; |
808 |
case 0x00007fff: psize = 14; break; |
case 0x00007fff: psize = 14; break; |
814 |
case 0x07ffffff: psize = 26; break; |
case 0x07ffffff: psize = 26; break; |
815 |
default: |
default: |
816 |
printf("invalidate_translation_caches" |
printf("invalidate_translation_caches" |
817 |
"_paddr(): bad pagemask?\n"); |
"_paddr(): bad pagemask: 0x%x\n", |
818 |
|
(int)tmp_pmask); |
819 |
} |
} |
820 |
tlb_paddr0 = (cpu->cd.mips.coproc[0]->tlbs[i]. |
tlb_paddr0 = (cpu->cd.mips.coproc[0]->tlbs[i]. |
821 |
lo0 & ENTRYLO_PFN_MASK)>>ENTRYLO_PFN_SHIFT; |
lo0 & ENTRYLO_PFN_MASK)>>ENTRYLO_PFN_SHIFT; |
1016 |
if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_WIRED) unimpl = 0; |
1017 |
if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_BADVADDR) unimpl = 0; |
1018 |
if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { |
if (cp->coproc_nr==0 && reg_nr==COP0_COUNT) { |
1019 |
|
#if 0 |
1020 |
/* |
/* |
1021 |
* This speeds up delay-loops that just read the count |
* This speeds up delay-loops that just read the count |
1022 |
* register until it has reached a certain value. (Only for |
* register until it has reached a certain value. (Only for |
1037 |
cp->reg[COP0_COUNT] = (int64_t) |
cp->reg[COP0_COUNT] = (int64_t) |
1038 |
(int32_t)(cp->reg[COP0_COUNT] + increase); |
(int32_t)(cp->reg[COP0_COUNT] + increase); |
1039 |
} |
} |
1040 |
|
#endif |
1041 |
unimpl = 0; |
unimpl = 0; |
1042 |
} |
} |
1043 |
if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; |
if (cp->coproc_nr==0 && reg_nr==COP0_ENTRYHI) unimpl = 0; |
1426 |
* |
* |
1427 |
* TODO: Move this to some other file? |
* TODO: Move this to some other file? |
1428 |
*/ |
*/ |
1429 |
|
static int mips_fmt_to_ieee_fmt[32] = { |
1430 |
|
0, 0, 0, 0, 0, 0, 0, 0, |
1431 |
|
0, 0, 0, 0, 0, 0, 0, 0, |
1432 |
|
IEEE_FMT_S, IEEE_FMT_D, 0, 0, |
1433 |
|
IEEE_FMT_W, IEEE_FMT_L, /* PS (Paired Single) */ 0, 0, |
1434 |
|
0, 0, 0, 0, 0, 0, 0, 0 }; |
1435 |
|
|
1436 |
|
/* MIPS floating point types: */ |
1437 |
#define FMT_S 16 |
#define FMT_S 16 |
1438 |
#define FMT_D 17 |
#define FMT_D 17 |
1439 |
#define FMT_W 20 |
#define FMT_W 20 |
1450 |
#define FPU_OP_C 8 |
#define FPU_OP_C 8 |
1451 |
#define FPU_OP_ABS 9 |
#define FPU_OP_ABS 9 |
1452 |
#define FPU_OP_NEG 10 |
#define FPU_OP_NEG 10 |
1453 |
/* TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W, |
/* TODO: CEIL.L, CEIL.W, FLOOR.L, FLOOR.W, RECIP, ROUND.L, ROUND.W, RSQRT */ |
|
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<n_frac; i++) { |
|
|
int bit = (reg >> 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); */ |
|
|
} |
|
1454 |
|
|
1455 |
|
|
1456 |
/* |
/* |
1461 |
static void fpu_store_float_value(struct mips_coproc *cp, int fd, |
static void fpu_store_float_value(struct mips_coproc *cp, int fd, |
1462 |
double nf, int fmt, int nan) |
double nf, int fmt, int nan) |
1463 |
{ |
{ |
1464 |
int n_frac = 0, n_exp = 0, signofs=0; |
int ieee_fmt = mips_fmt_to_ieee_fmt[fmt]; |
1465 |
int i, exponent; |
uint64_t r = ieee_store_float_value(nf, ieee_fmt, nan); |
|
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; |
|
|
} |
|
1466 |
|
|
1467 |
/* |
/* |
1468 |
* TODO: this is for 32-bit mode. It has to be updated later |
* TODO: This is for 32-bit mode. It has to be updated later |
1469 |
* for 64-bit coprocessor stuff. |
* for 64-bit coprocessor functionality! |
1470 |
*/ |
*/ |
1471 |
if (fmt == FMT_D || fmt == FMT_L) { |
if (fmt == FMT_D || fmt == FMT_L) { |
1472 |
cp->reg[fd] = r & 0xffffffffULL; |
cp->reg[fd] = r & 0xffffffffULL; |
1488 |
/* |
/* |
1489 |
* fpu_op(): |
* fpu_op(): |
1490 |
* |
* |
1491 |
* Perform a floating-point operation. For those of fs and ft |
* Perform a floating-point operation. For those of fs and ft that are >= 0, |
1492 |
* that are >= 0, those numbers are interpreted into local |
* those numbers are interpreted into local variables. |
|
* variables. |
|
1493 |
* |
* |
1494 |
* Only FPU_OP_C (compare) returns anything of interest, 1 for |
* Only FPU_OP_C (compare) returns anything of interest, 1 for true, 0 for |
1495 |
* true, 0 for false. |
* false. |
1496 |
*/ |
*/ |
1497 |
static int fpu_op(struct cpu *cpu, struct mips_coproc *cp, int op, int fmt, |
static int fpu_op(struct cpu *cpu, struct mips_coproc *cp, int op, int fmt, |
1498 |
int ft, int fs, int fd, int cond, int output_fmt) |
int ft, int fs, int fd, int cond, int output_fmt) |
1499 |
{ |
{ |
1500 |
/* Potentially two input registers, fs and ft */ |
/* Potentially two input registers, fs and ft */ |
1501 |
struct internal_float_value float_value[2]; |
struct ieee_float_value float_value[2]; |
1502 |
int unordered, nan; |
int unordered, nan, ieee_fmt = mips_fmt_to_ieee_fmt[fmt]; |
1503 |
uint64_t fs_v = 0; |
uint64_t fs_v = 0; |
1504 |
double nf; |
double nf; |
1505 |
|
|
1510 |
if (fmt == FMT_D || fmt == FMT_L) |
if (fmt == FMT_D || fmt == FMT_L) |
1511 |
fs_v = (fs_v & 0xffffffffULL) + |
fs_v = (fs_v & 0xffffffffULL) + |
1512 |
(cp->reg[(fs + 1) & 31] << 32); |
(cp->reg[(fs + 1) & 31] << 32); |
1513 |
fpu_interpret_float_value(fs_v, &float_value[0], fmt); |
ieee_interpret_float_value(fs_v, &float_value[0], ieee_fmt); |
1514 |
} |
} |
1515 |
if (ft >= 0) { |
if (ft >= 0) { |
1516 |
uint64_t v = cp->reg[ft]; |
uint64_t v = cp->reg[ft]; |
1519 |
if (fmt == FMT_D || fmt == FMT_L) |
if (fmt == FMT_D || fmt == FMT_L) |
1520 |
v = (v & 0xffffffffULL) + |
v = (v & 0xffffffffULL) + |
1521 |
(cp->reg[(ft + 1) & 31] << 32); |
(cp->reg[(ft + 1) & 31] << 32); |
1522 |
fpu_interpret_float_value(v, &float_value[1], fmt); |
ieee_interpret_float_value(v, &float_value[1], ieee_fmt); |
1523 |
} |
} |
1524 |
|
|
1525 |
switch (op) { |
switch (op) { |