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.
8 #include "../port/lib.h"
18 /* undef this if correct kernel r13 isn't in Ureg;
19 * check calculation in fpiarm below
23 #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
24 #define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
26 typedef struct FP2 FP2;
27 typedef struct FP1 FP1;
31 void (*f)(Internal, Internal, Internal*);
36 void (*f)(Internal*, Internal*);
52 #define OFR(X) ((ulong)&((Ureg*)0)->X)
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),
61 static Internal fpconst[8] = { /* indexed by op&7 */
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 */
74 * arm binary operations
78 fadd(Internal m, Internal n, Internal *d)
80 (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
84 fsub(Internal m, Internal n, Internal *d)
87 (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
91 fsubr(Internal m, Internal n, Internal *d)
94 (n.s == m.s? fpiadd: fpisub)(&n, &m, d);
98 fmul(Internal m, Internal n, Internal *d)
104 fdiv(Internal m, Internal n, Internal *d)
110 fdivr(Internal m, Internal n, Internal *d)
116 * arm unary operations
120 fmov(Internal *m, Internal *d)
126 fmovn(Internal *m, Internal *d)
133 fabsf(Internal *m, Internal *d)
140 frnd(Internal *m, Internal *d)
144 (m->s? fsub: fadd)(fpconst[6], *m, d);
148 e = (d->e - ExpBias) + 1;
151 else if(e > FractBits){
153 d->l &= ~((1<<(2*FractBits - e))-1);
157 d->h &= ~((1<<(FractBits-e))-1);
161 static FP1 optab1[16] = { /* Fd := OP Fm */
166 [4] {"SQTF", /*fsqt*/0},
167 /* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
168 /* URD and NRM aren't implemented */
171 static FP2 optab2[16] = { /* Fd := Fn OP Fm */
175 [3] {"RSUBF", fsubr},
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 */
187 fcmp(Internal *n, Internal *m)
192 if(IsWeird(m) || IsWeird(n)){
193 /* BUG: should trap if not masked */
200 i = fpicmp(&rn, &rm);
210 fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
215 (*f)(&FR(ufp, d), mem);
217 print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
221 fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
229 print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea);
234 condok(int cc, int c)
239 case 1: /* Z clear */
243 case 3: /* C clear */
247 case 5: /* N clear */
251 case 7: /* V clear */
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 */
267 case 15: /* never (reserved) */
270 return 0; /* not reached */
274 unimp(ulong pc, ulong op)
278 snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
280 print("FPE: %s\n", buf);
286 fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
291 Internal tmp, *fm, *fn;
293 /* note: would update fault status here if we noted numeric exceptions */
298 if(((op>>25)&7) == 6){
300 unimp(pc, op); /* packed or extended */
303 if((op & (1<<23)) == 0)
313 fld(fpid2i, rd, ea, 8, ufp);
315 fld(fpis2i, rd, ea, 4, ufp);
318 fst(fpii2d, ea, rd, 8, ufp);
320 fst(fpii2s, ea, rd, 4, ufp);
322 if((op & (1<<24)) == 0)
330 * CPRT/transfer, 10.3
338 if(rd == 15 && op & (1<<20)){
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);
358 case 5: /* CNF: Fn :: -Fm */
359 case 7: /* CNFE: Fn :: -Fm (with exception) */
362 ur->psr &= ~(N|C|Z|V);
363 ur->psr |= fcmp(fn, &tmp);
367 print("CMPF %c%d,F%ld =%#lux\n",
368 tag, rn, op&7, ur->psr>>28);
373 * other transfer, 10.3
375 switch((op>>20)&0xF){
380 fpiw2i(&FR(ufp, rn), ®(ur, rd));
382 print("MOVW[FD] R%d, F%d\n", rd, rn);
389 fpii2w(®(ur, rd), &tmp);
391 print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
393 case 2: /* FPSR := Rd */
394 ufp->status = REG(ur, rd);
396 print("MOVW R%d, FPSR\n", rd);
398 case 3: /* Rd := FPSR */
399 REG(ur, rd) = ufp->status;
401 print("MOVW FPSR, R%d\n", rd);
403 case 4: /* FPCR := Rd */
404 ufp->control = REG(ur, rd);
406 print("MOVW R%d, FPCR\n", rd);
408 case 5: /* Rd := FPCR */
409 REG(ur, rd) = ufp->control;
411 print("MOVW FPCR, R%d\n", rd);
421 if(op & (1<<3)){ /* constant */
432 if(op & (1<<15)){ /* monadic */
438 print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd);
439 (*fp->f)(fm, &FR(ufp, rd));
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));
453 casemu(ulong pc, ulong op, Ureg *ur)
455 ulong *rp, ro, rn, *rd;
460 ro = rp[op>>16 & 0x7];
461 rn = rp[op>>0 & 0x7];
462 rd = rp + (op>>12 & 0x7);
464 validaddr((ulong)rp, 4, 1);
466 if(*rd = (*rp == ro))
474 ldrex(ulong pc, ulong op, Ureg *ur)
476 ulong *rp, *rd, *addr;
481 rd = rp + (op>>16 & 0x7);
483 validaddr((ulong)addr, 4, 0);
485 rp[op>>12 & 0x7] = *addr;
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]);
492 strex(ulong pc, ulong op, Ureg *ur)
494 ulong *rp, rn, *rd, *addr;
499 rd = rp + (op>>16 & 0x7);
500 rn = rp[op>>0 & 0x7];
502 validaddr((ulong)addr, 4, 1);
506 print("strex valid, [r%ld]@0x%8.8p = r%ld = 0x%8.8lux",
507 op>>16 & 0x7, addr, op>>0 & 0x7, rn);
510 rp[op>>12 & 0x7] = 0;
513 print("strex invalid, r%ld = 1", op>>16 & 0x7);
514 rp[op>>12 & 0x7] = 1;
522 void (*f)(ulong, ulong, Ureg*);
524 { 0x01900f9f, 0x0ff00fff, ldrex },
525 { 0x01800f90, 0x0ff00ff0, strex },
526 { 0x0ed00100, 0x0ef08100, casemu },
527 { 0x00000000, 0x00000000, nil }
531 * returns the number of FP instructions emulated
541 panic("fpiarm not in a process");
543 /* because all the state is in the proc structure,
544 * it need not be saved/restored
546 if(up->fpstate != FPactive){
547 // assert(sizeof(Internal) == sizeof(ufp->regs[0]));
548 up->fpstate = FPactive;
550 ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */
551 for(n = 0; n < 8; n++)
552 FR(ufp, n) = fpconst[0];
555 validaddr(ur->pc, 4, 0);
556 op = *(ulong*)(ur->pc);
558 print("%#lux: %#8.8lux ", ur->pc, op);
560 if(condok(ur->psr, op>>28)){
561 for(i = 0; specialopc[i].f; i++)
562 if((op & specialopc[i].mask) == specialopc[i].opc)
565 specialopc[i].f(ur->pc, op, ur);
566 else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
569 fpemu(ur->pc, op, ur, ufp);
570 }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
574 if(fpemudebug) print("\n");