diff options
Diffstat (limited to 'sim/mips/cp1.c')
-rw-r--r-- | sim/mips/cp1.c | 266 |
1 files changed, 176 insertions, 90 deletions
diff --git a/sim/mips/cp1.c b/sim/mips/cp1.c index 442c311..3641df7 100644 --- a/sim/mips/cp1.c +++ b/sim/mips/cp1.c @@ -40,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "sim-main.h" -#include "sim-fpu.h" /* Within cp1.c we refer to sim_cpu directly. */ #define CPU cpu @@ -313,77 +312,163 @@ store_fpr (sim_cpu *cpu, return; } -int -NaN (op, fmt) - uword64 op; - FP_formats fmt; + +/* CP1 control/status registers */ + +void +test_fcsr (sim_cpu *cpu, + address_word cia) { - int boolean = 0; - switch (fmt) + unsigned int cause; + + cause = (FCSR & fcsr_CAUSE_mask) >> fcsr_CAUSE_shift; + if ((cause & ((FCSR & fcsr_ENABLES_mask) >> fcsr_ENABLES_shift)) != 0 + || (cause & (1 << UO))) { - case fmt_single: - case fmt_word: - { - sim_fpu wop; - sim_fpu_32to (&wop, op); - boolean = sim_fpu_is_nan (&wop); - break; - } - case fmt_double: - case fmt_long: - { - sim_fpu wop; - sim_fpu_64to (&wop, op); - boolean = sim_fpu_is_nan (&wop); - break; - } - default: - fprintf (stderr, "Bad switch\n"); - abort (); + SignalExceptionFPE(); } +} -#ifdef DEBUG - printf ("DBG: NaN: returning %d for 0x%s (format = %s)\n", - boolean, pr_addr (op), fpu_format_name (fmt)); -#endif /* DEBUG */ +unsigned_word +value_fcr(sim_cpu *cpu, + address_word cia, + int fcr) +{ + unsigned32 value = 0; + + switch (fcr) + { + case 0: /* FP Implementation and Revision Register */ + value = FCR0; + break; + case 25: /* FP Condition Codes Register */ + value = (FCR31 & fcsr_FCC_mask) >> fcsr_FCC_shift; + value = (value & 0x1) | (value >> 1); /* close FCC gap */ + break; + case 26: /* FP Exceptions Register */ + value = FCR31 & (fcsr_CAUSE_mask | fcsr_FLAGS_mask); + break; + case 28: /* FP Enables Register */ + value = FCR31 & (fcsr_ENABLES_mask | fcsr_RM_mask); + if (FCR31 & fcsr_FS) + value |= 0x4; /* nonstandard FS bit */ + break; + case 31: /* FP Control/Status Register */ + value = FCR31 & ~fcsr_ZERO_mask; + break; + } - return (boolean); + return (EXTEND32 (value)); } -int -Less (op1, op2, fmt) - uword64 op1; - uword64 op2; - FP_formats fmt; +void +store_fcr(sim_cpu *cpu, + address_word cia, + int fcr, + unsigned_word value) { - int boolean = 0; + unsigned32 v; - /* Argument checking already performed by the FPCOMPARE code */ + v = VL4_8(value); + switch (fcr) + { + case 25: /* FP Condition Codes Register */ + v = (v << 1) | (v & 0x1); /* adjust for FCC gap */ + FCR31 &= ~fcsr_FCC_mask; + FCR31 |= ((v << fcsr_FCC_shift) & fcsr_FCC_mask); + break; + case 26: /* FP Exceptions Register */ + FCR31 &= ~(fcsr_CAUSE_mask | fcsr_FLAGS_mask); + FCR31 |= (v & (fcsr_CAUSE_mask | fcsr_FLAGS_mask)); + test_fcsr(cpu, cia); + break; + case 28: /* FP Enables Register */ + if (v & 0x4) /* nonstandard FS bit */ + v |= fcsr_FS; + else + v &= ~fcsr_FS; + FCR31 &= (fcsr_FCC_mask | fcsr_CAUSE_mask | fcsr_FLAGS_mask); + FCR31 |= (v & (fcsr_FS | fcsr_ENABLES_mask | fcsr_RM_mask)); + test_fcsr(cpu, cia); + break; + case 31: /* FP Control/Status Register */ + FCR31 = v & ~fcsr_ZERO_mask; + test_fcsr(cpu, cia); + break; + } +} -#ifdef DEBUG - printf ("DBG: Less: %s: op1 = 0x%s : op2 = 0x%s\n", - fpu_format_name (fmt), pr_addr (op1), pr_addr (op2)); -#endif /* DEBUG */ +void +update_fcsr (sim_cpu *cpu, + address_word cia, + sim_fpu_status status) +{ + FCSR &= ~fcsr_CAUSE_mask; - /* The format type should already have been checked: */ + if (status != 0) + { + unsigned int cause = 0; + + /* map between sim_fpu codes and MIPS FCSR */ + if (status & (sim_fpu_status_invalid_snan + | sim_fpu_status_invalid_isi + | sim_fpu_status_invalid_idi + | sim_fpu_status_invalid_zdz + | sim_fpu_status_invalid_imz + | sim_fpu_status_invalid_cmp + | sim_fpu_status_invalid_sqrt + | sim_fpu_status_invalid_cvi)) + cause |= (1 << IO); + if (status & sim_fpu_status_invalid_div0) + cause |= (1 << DZ); + if (status & sim_fpu_status_overflow) + cause |= (1 << OF); + if (status & sim_fpu_status_underflow) + cause |= (1 << UF); + if (status & sim_fpu_status_inexact) + cause |= (1 << IR); +#if 0 /* Not yet. */ + /* Implicit clearing of other bits by unimplemented done by callers. */ + if (status & sim_fpu_status_unimplemented) + cause |= (1 << UO); +#endif + + FCSR |= (cause << fcsr_CAUSE_shift); + test_fcsr (cpu, cia); + FCSR |= ((cause & ~(1 << UO)) << fcsr_FLAGS_shift); + } + return; +} + + +/* Comparison operations. */ + +static sim_fpu_status +fp_test(unsigned64 op1, + unsigned64 op2, + FP_formats fmt, + int abs, + int cond, + int *condition) +{ + sim_fpu wop1; + sim_fpu wop2; + sim_fpu_status status = 0; + int less, equal, unordered; + + /* The format type has already been checked: */ switch (fmt) { case fmt_single: { - sim_fpu wop1; - sim_fpu wop2; sim_fpu_32to (&wop1, op1); sim_fpu_32to (&wop2, op2); - boolean = sim_fpu_is_lt (&wop1, &wop2); break; } case fmt_double: { - sim_fpu wop1; - sim_fpu wop2; sim_fpu_64to (&wop1, op1); sim_fpu_64to (&wop2, op2); - boolean = sim_fpu_is_lt (&wop1, &wop2); break; } default: @@ -391,61 +476,60 @@ Less (op1, op2, fmt) abort (); } -#ifdef DEBUG - printf ("DBG: Less: returning %d (format = %s)\n", - boolean, fpu_format_name (fmt)); -#endif /* DEBUG */ - - return (boolean); + if (sim_fpu_is_nan (&wop1) || sim_fpu_is_nan (&wop2)) + { + if ((cond & (1 << 3)) || + sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2)) + status = sim_fpu_status_invalid_snan; + less = 0; + equal = 0; + unordered = 1; + } + else + { + if (abs) + { + status |= sim_fpu_abs (&wop1, &wop1); + status |= sim_fpu_abs (&wop2, &wop2); + } + equal = sim_fpu_is_eq (&wop1, &wop2); + less = !equal && sim_fpu_is_lt (&wop1, &wop2); + unordered = 0; + } + *condition = (((cond & (1 << 2)) && less) + || ((cond & (1 << 1)) && equal) + || ((cond & (1 << 0)) && unordered)); + return status; } -int -Equal (op1, op2, fmt) - uword64 op1; - uword64 op2; - FP_formats fmt; +void +fp_cmp(sim_cpu *cpu, + address_word cia, + unsigned64 op1, + unsigned64 op2, + FP_formats fmt, + int abs, + int cond, + int cc) { - int boolean = 0; - - /* Argument checking already performed by the FPCOMPARE code */ + sim_fpu_status status = 0; -#ifdef DEBUG - printf ("DBG: Equal: %s: op1 = 0x%s : op2 = 0x%s\n", - fpu_format_name (fmt), pr_addr (op1), pr_addr (op2)); -#endif /* DEBUG */ - - /* The format type should already have been checked: */ + /* The format type should already have been checked: */ switch (fmt) { case fmt_single: - { - sim_fpu wop1; - sim_fpu wop2; - sim_fpu_32to (&wop1, op1); - sim_fpu_32to (&wop2, op2); - boolean = sim_fpu_is_eq (&wop1, &wop2); - break; - } case fmt_double: { - sim_fpu wop1; - sim_fpu wop2; - sim_fpu_64to (&wop1, op1); - sim_fpu_64to (&wop2, op2); - boolean = sim_fpu_is_eq (&wop1, &wop2); + int result; + status = fp_test(op1, op2, fmt, abs, cond, &result); + update_fcsr (cpu, cia, status); + SETFCC (cc, result); break; } default: - fprintf (stderr, "Bad switch\n"); + sim_io_eprintf (SD, "Bad switch\n"); abort (); } - -#ifdef DEBUG - printf ("DBG: Equal: returning %d (format = %s)\n", - boolean, fpu_format_name (fmt)); -#endif /* DEBUG */ - - return (boolean); } @@ -613,6 +697,8 @@ fp_sqrt(sim_cpu *cpu, } +/* Conversion operations. */ + uword64 convert (sim_cpu *cpu, address_word cia, |