1 /* 2 * this doesn't attempt to implement ARM floating-point properties 3 * that aren't visible in the Inferno environment. 4 * all arithmetic is done in double precision. 5 * the FP trap status isn't updated. 6 */ 7 #include "u.h" 8 #include "../port/lib.h" 9 #include "mem.h" 10 #include "dat.h" 11 #include "fns.h" 12 13 #include "ureg.h" 14 15 #include "arm.h" 16 #include "../port/fpi.h" 17 18 /* undef this if correct kernel r13 isn't in Ureg; 19 * check calculation in fpiarm below 20 */ 21 22 23 #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)])) 24 #define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7]) 25 26 typedef struct FP2 FP2; 27 typedef struct FP1 FP1; 28 29 struct FP2 { 30 char* name; 31 void (*f)(Internal, Internal, Internal*); 32 }; 33 34 struct FP1 { 35 char* name; 36 void (*f)(Internal*, Internal*); 37 }; 38 39 enum { 40 N = 1<<31, 41 Z = 1<<30, 42 C = 1<<29, 43 V = 1<<28, 44 REGPC = 15, 45 }; 46 47 enum { 48 fpemudebug = 0, 49 }; 50 51 #undef OFR 52 #define OFR(X) ((ulong)&((Ureg*)0)->X) 53 54 static int roff[] = { 55 OFR(r0), OFR(r1), OFR(r2), OFR(r3), 56 OFR(r4), OFR(r5), OFR(r6), OFR(r7), 57 OFR(r8), OFR(r9), OFR(r10), OFR(r11), 58 OFR(r12), OFR(r13), OFR(r14), OFR(pc), 59 }; 60 61 static Internal fpconst[8] = { /* indexed by op&7 */ 62 /* s, e, l, h */ 63 {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ 64 {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ 65 {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ 66 {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */ 67 {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */ 68 {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */ 69 {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ 70 {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */ 71 }; 72 73 /* 74 * arm binary operations 75 */ 76 77 static void 78 fadd(Internal m, Internal n, Internal *d) 79 { 80 (m.s == n.s? fpiadd: fpisub)(&m, &n, d); 81 } 82 83 static void 84 fsub(Internal m, Internal n, Internal *d) 85 { 86 m.s ^= 1; 87 (m.s == n.s? fpiadd: fpisub)(&m, &n, d); 88 } 89 90 static void 91 fsubr(Internal m, Internal n, Internal *d) 92 { 93 n.s ^= 1; 94 (n.s == m.s? fpiadd: fpisub)(&n, &m, d); 95 } 96 97 static void 98 fmul(Internal m, Internal n, Internal *d) 99 { 100 fpimul(&m, &n, d); 101 } 102 103 static void 104 fdiv(Internal m, Internal n, Internal *d) 105 { 106 fpidiv(&m, &n, d); 107 } 108 109 static void 110 fdivr(Internal m, Internal n, Internal *d) 111 { 112 fpidiv(&n, &m, d); 113 } 114 115 /* 116 * arm unary operations 117 */ 118 119 static void 120 fmov(Internal *m, Internal *d) 121 { 122 *d = *m; 123 } 124 125 static void 126 fmovn(Internal *m, Internal *d) 127 { 128 *d = *m; 129 d->s ^= 1; 130 } 131 132 static void 133 fabsf(Internal *m, Internal *d) 134 { 135 *d = *m; 136 d->s = 0; 137 } 138 139 static void 140 frnd(Internal *m, Internal *d) 141 { 142 short e; 143 144 (m->s? fsub: fadd)(fpconst[6], *m, d); 145 if(IsWeird(d)) 146 return; 147 fpiround(d); 148 e = (d->e - ExpBias) + 1; 149 if(e <= 0) 150 SetZero(d); 151 else if(e > FractBits){ 152 if(e < 2*FractBits) 153 d->l &= ~((1<<(2*FractBits - e))-1); 154 }else{ 155 d->l = 0; 156 if(e < FractBits) 157 d->h &= ~((1<<(FractBits-e))-1); 158 } 159 } 160 161 static FP1 optab1[16] = { /* Fd := OP Fm */ 162 [0] {"MOVF", fmov}, 163 [1] {"NEGF", fmovn}, 164 [2] {"ABSF", fabsf}, 165 [3] {"RNDF", frnd}, 166 [4] {"SQTF", /*fsqt*/0}, 167 /* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */ 168 /* URD and NRM aren't implemented */ 169 }; 170 171 static FP2 optab2[16] = { /* Fd := Fn OP Fm */ 172 [0] {"ADDF", fadd}, 173 [1] {"MULF", fmul}, 174 [2] {"SUBF", fsub}, 175 [3] {"RSUBF", fsubr}, 176 [4] {"DIVF", fdiv}, 177 [5] {"RDIVF", fdivr}, 178 /* POW, RPW deprecated */ 179 [8] {"REMF", /*frem*/0}, 180 [9] {"FMF", fmul}, /* fast multiply */ 181 [10] {"FDV", fdiv}, /* fast divide */ 182 [11] {"FRD", fdivr}, /* fast reverse divide */ 183 /* POL deprecated */ 184 }; 185 186 static ulong 187 fcmp(Internal *n, Internal *m) 188 { 189 int i; 190 Internal rm, rn; 191 192 if(IsWeird(m) || IsWeird(n)){ 193 /* BUG: should trap if not masked */ 194 return V|C; 195 } 196 rn = *n; 197 rm = *m; 198 fpiround(&rn); 199 fpiround(&rm); 200 i = fpicmp(&rn, &rm); 201 if(i > 0) 202 return C; 203 else if(i == 0) 204 return C|Z; 205 else 206 return N; 207 } 208 209 static void 210 fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp) 211 { 212 void *mem; 213 214 mem = (void*)ea; 215 (*f)(&FR(ufp, d), mem); 216 if(fpemudebug) 217 print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d); 218 } 219 220 static void 221 fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp) 222 { 223 Internal tmp; 224 void *mem; 225 226 mem = (void*)ea; 227 tmp = FR(ufp, s); 228 if(fpemudebug) 229 print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea); 230 (*f)(mem, &tmp); 231 } 232 233 static int 234 condok(int cc, int c) 235 { 236 switch(c){ 237 case 0: /* Z set */ 238 return cc&Z; 239 case 1: /* Z clear */ 240 return (cc&Z) == 0; 241 case 2: /* C set */ 242 return cc&C; 243 case 3: /* C clear */ 244 return (cc&C) == 0; 245 case 4: /* N set */ 246 return cc&N; 247 case 5: /* N clear */ 248 return (cc&N) == 0; 249 case 6: /* V set */ 250 return cc&V; 251 case 7: /* V clear */ 252 return (cc&V) == 0; 253 case 8: /* C set and Z clear */ 254 return cc&C && (cc&Z) == 0; 255 case 9: /* C clear or Z set */ 256 return (cc&C) == 0 || cc&Z; 257 case 10: /* N set and V set, or N clear and V clear */ 258 return (~cc&(N|V))==0 || (cc&(N|V)) == 0; 259 case 11: /* N set and V clear, or N clear and V set */ 260 return (cc&(N|V))==N || (cc&(N|V))==V; 261 case 12: /* Z clear, and either N set and V set or N clear and V clear */ 262 return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0); 263 case 13: /* Z set, or N set and V clear or N clear and V set */ 264 return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V; 265 case 14: /* always */ 266 return 1; 267 case 15: /* never (reserved) */ 268 return 0; 269 } 270 return 0; /* not reached */ 271 } 272 273 static void 274 unimp(ulong pc, ulong op) 275 { 276 char buf[60]; 277 278 snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op); 279 if(fpemudebug) 280 print("FPE: %s\n", buf); 281 error(buf); 282 /* no return */ 283 } 284 285 static void 286 fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) 287 { 288 int rn, rd, tag, o; 289 long off; 290 ulong ea; 291 Internal tmp, *fm, *fn; 292 293 /* note: would update fault status here if we noted numeric exceptions */ 294 295 /* 296 * LDF, STF; 10.1.1 297 */ 298 if(((op>>25)&7) == 6){ 299 if(op & (1<<22)) 300 unimp(pc, op); /* packed or extended */ 301 rn = (op>>16)&0xF; 302 off = (op&0xFF)<<2; 303 if((op & (1<<23)) == 0) 304 off = -off; 305 ea = REG(ur, rn); 306 if(rn == REGPC) 307 ea += 8; 308 if(op & (1<<24)) 309 ea += off; 310 rd = (op>>12)&7; 311 if(op & (1<<20)){ 312 if(op & (1<<15)) 313 fld(fpid2i, rd, ea, 8, ufp); 314 else 315 fld(fpis2i, rd, ea, 4, ufp); 316 }else{ 317 if(op & (1<<15)) 318 fst(fpii2d, ea, rd, 8, ufp); 319 else 320 fst(fpii2s, ea, rd, 4, ufp); 321 } 322 if((op & (1<<24)) == 0) 323 ea += off; 324 if(op & (1<<21)) 325 REG(ur, rn) = ea; 326 return; 327 } 328 329 /* 330 * CPRT/transfer, 10.3 331 */ 332 if(op & (1<<4)){ 333 rd = (op>>12) & 0xF; 334 335 /* 336 * compare, 10.3.1 337 */ 338 if(rd == 15 && op & (1<<20)){ 339 rn = (op>>16)&7; 340 fn = &FR(ufp, rn); 341 if(op & (1<<3)){ 342 fm = &fpconst[op&7]; 343 if(fpemudebug) 344 tag = 'C'; 345 }else{ 346 fm = &FR(ufp, op&7); 347 if(fpemudebug) 348 tag = 'F'; 349 } 350 switch((op>>21)&7){ 351 default: 352 unimp(pc, op); 353 case 4: /* CMF: Fn :: Fm */ 354 case 6: /* CMFE: Fn :: Fm (with exception) */ 355 ur->psr &= ~(N|C|Z|V); 356 ur->psr |= fcmp(fn, fm); 357 break; 358 case 5: /* CNF: Fn :: -Fm */ 359 case 7: /* CNFE: Fn :: -Fm (with exception) */ 360 tmp = *fm; 361 tmp.s ^= 1; 362 ur->psr &= ~(N|C|Z|V); 363 ur->psr |= fcmp(fn, &tmp); 364 break; 365 } 366 if(fpemudebug) 367 print("CMPF %c%d,F%ld =%#lux\n", 368 tag, rn, op&7, ur->psr>>28); 369 return; 370 } 371 372 /* 373 * other transfer, 10.3 374 */ 375 switch((op>>20)&0xF){ 376 default: 377 unimp(pc, op); 378 case 0: /* FLT */ 379 rn = (op>>16) & 7; 380 fpiw2i(&FR(ufp, rn), ®(ur, rd)); 381 if(fpemudebug) 382 print("MOVW[FD] R%d, F%d\n", rd, rn); 383 break; 384 case 1: /* FIX */ 385 if(op & (1<<3)) 386 unimp(pc, op); 387 rn = op & 7; 388 tmp = FR(ufp, rn); 389 fpii2w(®(ur, rd), &tmp); 390 if(fpemudebug) 391 print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd)); 392 break; 393 case 2: /* FPSR := Rd */ 394 ufp->status = REG(ur, rd); 395 if(fpemudebug) 396 print("MOVW R%d, FPSR\n", rd); 397 break; 398 case 3: /* Rd := FPSR */ 399 REG(ur, rd) = ufp->status; 400 if(fpemudebug) 401 print("MOVW FPSR, R%d\n", rd); 402 break; 403 case 4: /* FPCR := Rd */ 404 ufp->control = REG(ur, rd); 405 if(fpemudebug) 406 print("MOVW R%d, FPCR\n", rd); 407 break; 408 case 5: /* Rd := FPCR */ 409 REG(ur, rd) = ufp->control; 410 if(fpemudebug) 411 print("MOVW FPCR, R%d\n", rd); 412 break; 413 } 414 return; 415 } 416 417 /* 418 * arithmetic 419 */ 420 421 if(op & (1<<3)){ /* constant */ 422 fm = &fpconst[op&7]; 423 if(fpemudebug) 424 tag = 'C'; 425 }else{ 426 fm = &FR(ufp, op&7); 427 if(fpemudebug) 428 tag = 'F'; 429 } 430 rd = (op>>12)&7; 431 o = (op>>20)&0xF; 432 if(op & (1<<15)){ /* monadic */ 433 FP1 *fp; 434 fp = &optab1[o]; 435 if(fp->f == nil) 436 unimp(pc, op); 437 if(fpemudebug) 438 print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd); 439 (*fp->f)(fm, &FR(ufp, rd)); 440 } else { 441 FP2 *fp; 442 fp = &optab2[o]; 443 if(fp->f == nil) 444 unimp(pc, op); 445 rn = (op>>16)&7; 446 if(fpemudebug) 447 print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd); 448 (*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd)); 449 } 450 } 451 452 void 453 casemu(ulong pc, ulong op, Ureg *ur) 454 { 455 ulong *rp, ro, rn, *rd; 456 457 USED(pc); 458 459 rp = (ulong*)ur; 460 ro = rp[op>>16 & 0x7]; 461 rn = rp[op>>0 & 0x7]; 462 rd = rp + (op>>12 & 0x7); 463 rp = (ulong*)*rd; 464 validaddr((ulong)rp, 4, 1); 465 splhi(); 466 if(*rd = (*rp == ro)) 467 *rp = rn; 468 spllo(); 469 } 470 471 int ldrexvalid; 472 473 void 474 ldrex(ulong pc, ulong op, Ureg *ur) 475 { 476 ulong *rp, *rd, *addr; 477 478 USED(pc); 479 480 rp = (ulong*)ur; 481 rd = rp + (op>>16 & 0x7); 482 addr = (ulong*)*rd; 483 validaddr((ulong)addr, 4, 0); 484 ldrexvalid = 1; 485 rp[op>>12 & 0x7] = *addr; 486 if(fpemudebug) 487 print("ldrex, r%ld = [r%ld]@0x%8.8p = 0x%8.8lux", 488 op>>12 & 0x7, op>>16 & 0x7, addr, rp[op>>12 & 0x7]); 489 } 490 491 void 492 clrex(ulong, ulong, Ureg *) 493 { 494 ldrexvalid = 0; 495 if(fpemudebug) 496 print("clrex"); 497 } 498 499 void 500 strex(ulong pc, ulong op, Ureg *ur) 501 { 502 ulong *rp, rn, *rd, *addr; 503 504 USED(pc); 505 506 rp = (ulong*)ur; 507 rd = rp + (op>>16 & 0x7); 508 rn = rp[op>>0 & 0x7]; 509 addr = (ulong*)*rd; 510 validaddr((ulong)addr, 4, 1); 511 splhi(); 512 if(ldrexvalid){ 513 if(fpemudebug) 514 print("strex valid, [r%ld]@0x%8.8p = r%ld = 0x%8.8lux", 515 op>>16 & 0x7, addr, op>>0 & 0x7, rn); 516 *addr = rn; 517 ldrexvalid = 0; 518 rp[op>>12 & 0x7] = 0; 519 }else{ 520 if(fpemudebug) 521 print("strex invalid, r%ld = 1", op>>16 & 0x7); 522 rp[op>>12 & 0x7] = 1; 523 } 524 spllo(); 525 } 526 527 struct { 528 ulong opc; 529 ulong mask; 530 void (*f)(ulong, ulong, Ureg*); 531 } specialopc[] = { 532 { 0x01900f9f, 0x0ff00fff, ldrex }, 533 { 0x01800f90, 0x0ff00ff0, strex }, 534 { 0xf57ff01f, 0xffffffff, clrex }, 535 { 0x0ed00100, 0x0ef08100, casemu }, 536 { 0x00000000, 0x00000000, nil } 537 }; 538 539 /* 540 * returns the number of FP instructions emulated 541 */ 542 int 543 fpiarm(Ureg *ur) 544 { 545 ulong op, o; 546 FPsave *ufp; 547 int i, n; 548 549 if(up == nil) 550 panic("fpiarm not in a process"); 551 ufp = &up->fpsave; 552 /* because all the state is in the proc structure, 553 * it need not be saved/restored 554 */ 555 if(up->fpstate != FPactive){ 556 // assert(sizeof(Internal) == sizeof(ufp->regs[0])); 557 up->fpstate = FPactive; 558 ufp->control = 0; 559 ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ 560 for(n = 0; n < 8; n++) 561 FR(ufp, n) = fpconst[0]; 562 } 563 for(n=0; ;n++){ 564 validaddr(ur->pc, 4, 0); 565 op = *(ulong*)(ur->pc); 566 if(fpemudebug) 567 print("%#lux: %#8.8lux ", ur->pc, op); 568 o = (op>>24) & 0xF; 569 if(condok(ur->psr, op>>28)){ 570 for(i = 0; specialopc[i].f; i++) 571 if((op & specialopc[i].mask) == specialopc[i].opc) 572 break; 573 if(specialopc[i].f) 574 specialopc[i].f(ur->pc, op, ur); 575 else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) 576 break; 577 else 578 fpemu(ur->pc, op, ur, ufp); 579 }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) 580 break; 581 ur->pc += 4; 582 } 583 if(fpemudebug) print("\n"); 584 return n; 585 } 586