1 1.17 isaki /* $NetBSD: fpu_fscale.c,v 1.17 2025/01/06 07:34:24 isaki Exp $ */ 2 1.1 briggs 3 1.1 briggs /* 4 1.1 briggs * Copyright (c) 1995 Ken Nakata 5 1.1 briggs * All rights reserved. 6 1.1 briggs * 7 1.1 briggs * Redistribution and use in source and binary forms, with or without 8 1.1 briggs * modification, are permitted provided that the following conditions 9 1.1 briggs * are met: 10 1.1 briggs * 1. Redistributions of source code must retain the above copyright 11 1.1 briggs * notice, this list of conditions and the following disclaimer. 12 1.1 briggs * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 briggs * notice, this list of conditions and the following disclaimer in the 14 1.1 briggs * documentation and/or other materials provided with the distribution. 15 1.1 briggs * 3. The name of the author may not be used to endorse or promote products 16 1.1 briggs * derived from this software without specific prior written permission. 17 1.1 briggs * 4. All advertising materials mentioning features or use of this software 18 1.1 briggs * must display the following acknowledgement: 19 1.1 briggs * This product includes software developed by Gordon Ross 20 1.1 briggs * 21 1.1 briggs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 1.1 briggs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 1.1 briggs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 1.1 briggs * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 1.1 briggs * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 1.1 briggs * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 1.1 briggs * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 1.1 briggs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 1.1 briggs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 1.1 briggs * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 1.1 briggs */ 32 1.1 briggs 33 1.1 briggs /* 34 1.1 briggs * FSCALE - separated from the other type0 arithmetic instructions 35 1.1 briggs * for performance reason; maybe unnecessary, but FSCALE assumes 36 1.1 briggs * the source operand be an integer. It performs type conversion 37 1.1 briggs * only if the source operand is *not* an integer. 38 1.1 briggs */ 39 1.11 lukem 40 1.11 lukem #include <sys/cdefs.h> 41 1.17 isaki __KERNEL_RCSID(0, "$NetBSD: fpu_fscale.c,v 1.17 2025/01/06 07:34:24 isaki Exp $"); 42 1.1 briggs 43 1.1 briggs #include <sys/types.h> 44 1.1 briggs #include <sys/signal.h> 45 1.3 briggs #include <sys/systm.h> 46 1.1 briggs #include <machine/frame.h> 47 1.1 briggs 48 1.1 briggs #include "fpu_emulate.h" 49 1.1 briggs 50 1.1 briggs int 51 1.13 dsl fpu_emul_fscale(struct fpemu *fe, struct instruction *insn) 52 1.1 briggs { 53 1.14 isaki struct frame *frame; 54 1.16 isaki uint32_t *fpregs; 55 1.14 isaki int word1, sig; 56 1.14 isaki int regnum, format; 57 1.17 isaki int modreg; 58 1.14 isaki int scale, sign, exp; 59 1.16 isaki uint32_t m0, m1; 60 1.16 isaki uint32_t buf[3], fpsr; 61 1.10 briggs #if DEBUG_FPE 62 1.14 isaki int flags; 63 1.14 isaki char regname; 64 1.10 briggs #endif 65 1.1 briggs 66 1.14 isaki scale = sig = 0; 67 1.14 isaki frame = fe->fe_frame; 68 1.14 isaki fpregs = &(fe->fe_fpframe->fpf_regs[0]); 69 1.14 isaki /* clear all exceptions and conditions */ 70 1.14 isaki fpsr = fe->fe_fpsr & ~FPSR_EXCP & ~FPSR_CCB; 71 1.10 briggs #if DEBUG_FPE 72 1.14 isaki printf("fpu_emul_fscale: FPSR = %08x, FPCR = %08x\n", fpsr, fe->fe_fpcr); 73 1.10 briggs #endif 74 1.1 briggs 75 1.14 isaki word1 = insn->is_word1; 76 1.14 isaki format = (word1 >> 10) & 7; 77 1.14 isaki regnum = (word1 >> 7) & 7; 78 1.1 briggs 79 1.14 isaki fe->fe_fpcr &= FPCR_ROUND; 80 1.14 isaki fe->fe_fpcr |= FPCR_ZERO; 81 1.1 briggs 82 1.14 isaki /* get the source operand */ 83 1.14 isaki if ((word1 & 0x4000) == 0) { 84 1.10 briggs #if DEBUG_FPE 85 1.14 isaki printf("fpu_emul_fscale: FP%d op FP%d => FP%d\n", 86 1.15 isaki format, regnum, regnum); 87 1.14 isaki /* the operand is an FP reg */ 88 1.14 isaki printf("fpu_emul_scale: src opr FP%d=%08x%08x%08x\n", 89 1.15 isaki format, fpregs[format*3], fpregs[format*3+1], 90 1.15 isaki fpregs[format*3+2]); 91 1.10 briggs #endif 92 1.14 isaki fpu_explode(fe, &fe->fe_f2, FTYPE_EXT, &fpregs[format * 3]); 93 1.14 isaki fpu_implode(fe, &fe->fe_f2, FTYPE_LNG, buf); 94 1.14 isaki scale = buf[0]; 95 1.1 briggs } else { 96 1.14 isaki /* the operand is in memory */ 97 1.14 isaki if (format == FTYPE_DBL) { 98 1.14 isaki insn->is_datasize = 8; 99 1.14 isaki } else if (format == FTYPE_SNG || format == FTYPE_LNG) { 100 1.14 isaki insn->is_datasize = 4; 101 1.14 isaki } else if (format == FTYPE_WRD) { 102 1.14 isaki insn->is_datasize = 2; 103 1.14 isaki } else if (format == FTYPE_BYT) { 104 1.14 isaki insn->is_datasize = 1; 105 1.14 isaki } else if (format == FTYPE_EXT) { 106 1.14 isaki insn->is_datasize = 12; 107 1.14 isaki } else { 108 1.14 isaki /* invalid or unsupported operand format */ 109 1.14 isaki sig = SIGFPE; 110 1.14 isaki return sig; 111 1.14 isaki } 112 1.1 briggs 113 1.17 isaki /* Check an illegal mod/reg. */ 114 1.17 isaki modreg = insn->is_opcode & 077; 115 1.17 isaki if ((modreg >> 3) == 1/*An*/ || modreg >= 075) { 116 1.17 isaki return SIGILL; 117 1.17 isaki } 118 1.17 isaki 119 1.14 isaki /* Get effective address. (modreg=opcode&077) */ 120 1.14 isaki sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode); 121 1.14 isaki if (sig) { 122 1.10 briggs #if DEBUG_FPE 123 1.14 isaki printf("fpu_emul_fscale: error in decode_ea\n"); 124 1.10 briggs #endif 125 1.14 isaki return sig; 126 1.14 isaki } 127 1.1 briggs 128 1.17 isaki if (insn->is_ea.ea_flags == EA_DIRECT && 129 1.17 isaki insn->is_datasize > 4) { 130 1.17 isaki #if DEBUG_FPE 131 1.17 isaki printf("%s: attempted to fetch dbl/ext from reg\n", 132 1.17 isaki __func__); 133 1.17 isaki #endif 134 1.17 isaki return SIGILL; 135 1.17 isaki } 136 1.17 isaki 137 1.10 briggs #if DEBUG_FPE 138 1.14 isaki printf("fpu_emul_fscale: addr mode = "); 139 1.14 isaki flags = insn->is_ea.ea_flags; 140 1.14 isaki regname = (insn->is_ea.ea_regnum & 8) ? 'a' : 'd'; 141 1.14 isaki 142 1.14 isaki if (flags & EA_DIRECT) { 143 1.14 isaki printf("%c%d\n", regname, insn->is_ea.ea_regnum & 7); 144 1.14 isaki } else if (flags & EA_PREDECR) { 145 1.14 isaki printf("%c%d@-\n", regname, insn->is_ea.ea_regnum & 7); 146 1.14 isaki } else if (flags & EA_POSTINCR) { 147 1.14 isaki printf("%c%d@+\n", regname, insn->is_ea.ea_regnum & 7); 148 1.14 isaki } else if (flags & EA_OFFSET) { 149 1.14 isaki printf("%c%d@(%d)\n", 150 1.15 isaki regname, insn->is_ea.ea_regnum & 7, 151 1.15 isaki insn->is_ea.ea_offset); 152 1.14 isaki } else if (flags & EA_INDEXED) { 153 1.14 isaki printf("%c%d@(...)\n", 154 1.15 isaki regname, insn->is_ea.ea_regnum & 7); 155 1.14 isaki } else if (flags & EA_ABS) { 156 1.14 isaki printf("0x%08x\n", insn->is_ea.ea_absaddr); 157 1.14 isaki } else if (flags & EA_PC_REL) { 158 1.14 isaki printf("pc@(%d)\n", insn->is_ea.ea_offset); 159 1.14 isaki } else if (flags & EA_IMMED) { 160 1.14 isaki printf("#0x%08x%08x%08x\n", 161 1.15 isaki insn->is_ea.ea_immed[0], 162 1.15 isaki insn->is_ea.ea_immed[1], 163 1.15 isaki insn->is_ea.ea_immed[2]); 164 1.14 isaki } else { 165 1.14 isaki printf("%c%d@\n", regname, insn->is_ea.ea_regnum & 7); 166 1.14 isaki } 167 1.10 briggs #endif 168 1.14 isaki fpu_load_ea(frame, insn, &insn->is_ea, (char*)buf); 169 1.1 briggs 170 1.10 briggs #if DEBUG_FPE 171 1.14 isaki printf("fpu_emul_fscale: src = %08x%08x%08x, siz = %d\n", 172 1.15 isaki buf[0], buf[1], buf[2], insn->is_datasize); 173 1.10 briggs #endif 174 1.14 isaki if (format == FTYPE_LNG) { 175 1.14 isaki /* nothing */ 176 1.14 isaki scale = buf[0]; 177 1.14 isaki } else if (format == FTYPE_WRD) { 178 1.14 isaki /* sign-extend */ 179 1.14 isaki scale = buf[0] & 0xffff; 180 1.14 isaki if (scale & 0x8000) { 181 1.14 isaki scale |= 0xffff0000; 182 1.1 briggs } 183 1.14 isaki } else if (format == FTYPE_BYT) { 184 1.14 isaki /* sign-extend */ 185 1.14 isaki scale = buf[0] & 0xff; 186 1.14 isaki if (scale & 0x80) { 187 1.14 isaki scale |= 0xffffff00; 188 1.1 briggs } 189 1.14 isaki } else if (format == FTYPE_DBL || format == FTYPE_SNG || 190 1.14 isaki format == FTYPE_EXT) { 191 1.14 isaki fpu_explode(fe, &fe->fe_f2, format, buf); 192 1.14 isaki fpu_implode(fe, &fe->fe_f2, FTYPE_LNG, buf); 193 1.14 isaki scale = buf[0]; 194 1.1 briggs } 195 1.14 isaki /* make it look like we've got an FP oprand */ 196 1.14 isaki fe->fe_f2.fp_class = (buf[0] == 0) ? FPC_ZERO : FPC_NUM; 197 1.14 isaki } 198 1.14 isaki 199 1.14 isaki /* assume there's no exception */ 200 1.14 isaki sig = 0; 201 1.14 isaki 202 1.14 isaki /* 203 1.14 isaki * it's barbaric but we're going to operate directly on 204 1.14 isaki * the dst operand's bit pattern 205 1.14 isaki */ 206 1.14 isaki sign = fpregs[regnum * 3] & 0x80000000; 207 1.14 isaki exp = (fpregs[regnum * 3] & 0x7fff0000) >> 16; 208 1.14 isaki m0 = fpregs[regnum * 3 + 1]; 209 1.14 isaki m1 = fpregs[regnum * 3 + 2]; 210 1.14 isaki 211 1.14 isaki switch (fe->fe_f2.fp_class) { 212 1.14 isaki case FPC_SNAN: 213 1.14 isaki fpsr |= FPSR_SNAN; 214 1.14 isaki case FPC_QNAN: 215 1.14 isaki /* dst = NaN */ 216 1.14 isaki exp = 0x7fff; 217 1.14 isaki m0 = m1 = 0xffffffff; 218 1.14 isaki break; 219 1.14 isaki case FPC_ZERO: 220 1.14 isaki case FPC_NUM: 221 1.14 isaki if ((0 < exp && exp < 0x7fff) || 222 1.14 isaki (exp == 0 && (m0 | m1) != 0)) { 223 1.14 isaki /* normal or denormal */ 224 1.14 isaki exp += scale; 225 1.14 isaki if (exp < 0) { 226 1.14 isaki /* underflow */ 227 1.16 isaki uint32_t grs; /* guard, round and sticky */ 228 1.14 isaki 229 1.14 isaki exp = 0; 230 1.14 isaki grs = m1 << (32 + exp); 231 1.14 isaki m1 = m0 << (32 + exp) | m1 >> -exp; 232 1.14 isaki m0 >>= -exp; 233 1.14 isaki if (grs != 0) { 234 1.14 isaki fpsr |= FPSR_INEX2; 235 1.14 isaki 236 1.14 isaki switch (fe->fe_fpcr & 0x30) { 237 1.14 isaki case FPCR_MINF: 238 1.14 isaki if (sign != 0) { 239 1.14 isaki if (++m1 == 0 && 240 1.14 isaki ++m0 == 0) { 241 1.14 isaki m0 = 0x80000000; 242 1.14 isaki exp++; 243 1.14 isaki } 244 1.14 isaki } 245 1.14 isaki break; 246 1.14 isaki case FPCR_NEAR: 247 1.14 isaki if (grs == 0x80000000) { 248 1.14 isaki /* tie */ 249 1.14 isaki if ((m1 & 1) && 250 1.14 isaki ++m1 == 0 && 251 1.14 isaki ++m0 == 0) { 252 1.14 isaki m0 = 0x80000000; 253 1.14 isaki exp++; 254 1.14 isaki } 255 1.14 isaki } else if (grs & 0x80000000) { 256 1.14 isaki if (++m1 == 0 && 257 1.14 isaki ++m0 == 0) { 258 1.14 isaki m0 = 0x80000000; 259 1.14 isaki exp++; 260 1.14 isaki } 261 1.14 isaki } 262 1.14 isaki break; 263 1.14 isaki case FPCR_PINF: 264 1.14 isaki if (sign == 0) { 265 1.14 isaki if (++m1 == 0 && 266 1.14 isaki ++m0 == 0) { 267 1.14 isaki m0 = 0x80000000; 268 1.14 isaki exp++; 269 1.14 isaki } 270 1.14 isaki } 271 1.14 isaki break; 272 1.14 isaki case FPCR_ZERO: 273 1.14 isaki break; 274 1.14 isaki } 275 1.14 isaki } 276 1.14 isaki if (exp == 0 && (m0 & 0x80000000) == 0) { 277 1.14 isaki fpsr |= FPSR_UNFL; 278 1.14 isaki if ((m0 | m1) == 0) { 279 1.14 isaki fpsr |= FPSR_ZERO; 280 1.14 isaki } 281 1.14 isaki } 282 1.14 isaki } else if (exp >= 0x7fff) { 283 1.14 isaki /* overflow --> result = Inf */ 284 1.14 isaki /* 285 1.14 isaki * but first, try to normalize in case it's an 286 1.14 isaki * unnormalized 287 1.14 isaki */ 288 1.14 isaki while ((m0 & 0x80000000) == 0) { 289 1.14 isaki exp--; 290 1.14 isaki m0 = (m0 << 1) | (m1 >> 31); 291 1.14 isaki m1 = m1 << 1; 292 1.14 isaki } 293 1.14 isaki /* if it's still too large, then return Inf */ 294 1.14 isaki if (exp >= 0x7fff) { 295 1.14 isaki exp = 0x7fff; 296 1.14 isaki m0 = m1 = 0; 297 1.14 isaki fpsr |= FPSR_OVFL | FPSR_INF; 298 1.14 isaki } 299 1.14 isaki } else if ((m0 & 0x80000000) == 0) { 300 1.14 isaki /* 301 1.14 isaki * it's a denormal; we try to normalize but 302 1.14 isaki * result may and may not be a normal. 303 1.14 isaki */ 304 1.14 isaki while (exp > 0 && (m0 & 0x80000000) == 0) { 305 1.14 isaki exp--; 306 1.14 isaki m0 = (m0 << 1) | (m1 >> 31); 307 1.14 isaki m1 = m1 << 1; 308 1.14 isaki } 309 1.14 isaki if ((m0 & 0x80000000) == 0) { 310 1.14 isaki fpsr |= FPSR_UNFL; 311 1.14 isaki } 312 1.14 isaki } /* exp in range and mantissa normalized */ 313 1.14 isaki } else if (exp == 0 && m0 == 0 && m1 == 0) { 314 1.14 isaki /* dst is Zero */ 315 1.1 briggs fpsr |= FPSR_ZERO; 316 1.14 isaki } /* else we know exp == 0x7fff */ 317 1.14 isaki else if ((m0 | m1) == 0) { 318 1.14 isaki fpsr |= FPSR_INF; 319 1.14 isaki } else if ((m0 & 0x40000000) == 0) { 320 1.14 isaki /* a signaling NaN */ 321 1.14 isaki fpsr |= FPSR_NAN | FPSR_SNAN; 322 1.14 isaki } else { 323 1.14 isaki /* a quiet NaN */ 324 1.14 isaki fpsr |= FPSR_NAN; 325 1.1 briggs } 326 1.14 isaki break; 327 1.14 isaki case FPC_INF: 328 1.14 isaki /* dst = NaN */ 329 1.14 isaki exp = 0x7fff; 330 1.14 isaki m0 = m1 = 0xffffffff; 331 1.14 isaki fpsr |= FPSR_OPERR | FPSR_NAN; 332 1.14 isaki break; 333 1.14 isaki default: 334 1.1 briggs #ifdef DEBUG 335 1.14 isaki panic("fpu_emul_fscale: invalid fp class"); 336 1.1 briggs #endif 337 1.14 isaki break; 338 1.14 isaki } 339 1.1 briggs 340 1.14 isaki /* store the result */ 341 1.14 isaki fpregs[regnum * 3] = sign | (exp << 16); 342 1.14 isaki fpregs[regnum * 3 + 1] = m0; 343 1.14 isaki fpregs[regnum * 3 + 2] = m1; 344 1.14 isaki 345 1.14 isaki if (sign) { 346 1.14 isaki fpsr |= FPSR_NEG; 347 1.14 isaki } 348 1.1 briggs 349 1.14 isaki /* update fpsr according to the result of operation */ 350 1.14 isaki fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr; 351 1.1 briggs 352 1.10 briggs #if DEBUG_FPE 353 1.14 isaki printf("fpu_emul_fscale: FPSR = %08x, FPCR = %08x\n", 354 1.15 isaki fe->fe_fpsr, fe->fe_fpcr); 355 1.10 briggs #endif 356 1.1 briggs 357 1.14 isaki return (fpsr & fe->fe_fpcr & FPSR_EXCP) ? SIGFPE : sig; 358 1.1 briggs } 359