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
21 #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
22 #define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpctlregs - 1)])
24 typedef struct FP2 FP2;
25 typedef struct FP1 FP1;
29 void (*f)(Internal, Internal, Internal*);
34 void (*f)(Internal*, Internal*);
50 #define OFR(X) ((ulong)&((Ureg*)0)->X)
53 OFR(r0), OFR(r1), OFR(r2), OFR(r3),
54 OFR(r4), OFR(r5), OFR(r6), OFR(r7),
55 OFR(r8), OFR(r9), OFR(r10), OFR(r11),
56 OFR(r12), OFR(r13), OFR(r14), OFR(pc),
59 static Internal fpconst[8] = { /* indexed by op&7 (ARM 7500 FPA) */
61 {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
62 {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */
63 {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */
64 {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */
65 {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */
66 {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */
67 {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */
68 {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */
72 * arm binary operations
76 fadd(Internal m, Internal n, Internal *d)
78 (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
82 fsub(Internal m, Internal n, Internal *d)
85 (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
89 fsubr(Internal m, Internal n, Internal *d)
92 (n.s == m.s? fpiadd: fpisub)(&n, &m, d);
96 fmul(Internal m, Internal n, Internal *d)
102 fdiv(Internal m, Internal n, Internal *d)
108 fdivr(Internal m, Internal n, Internal *d)
114 * arm unary operations
118 fmov(Internal *m, Internal *d)
124 fmovn(Internal *m, Internal *d)
131 fabsf(Internal *m, Internal *d)
138 frnd(Internal *m, Internal *d)
142 (m->s? fsub: fadd)(fpconst[6], *m, d);
146 e = (d->e - ExpBias) + 1;
149 else if(e > FractBits){
151 d->l &= ~((1<<(2*FractBits - e))-1);
155 d->h &= ~((1<<(FractBits-e))-1);
160 * ARM 7500 FPA opcodes
163 static FP1 optab1[16] = { /* Fd := OP Fm */
168 [4] {"SQTF", /*fsqt*/0},
169 /* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
170 /* URD and NRM aren't implemented */
173 static FP2 optab2[16] = { /* Fd := Fn OP Fm */
177 [3] {"RSUBF", fsubr},
179 [5] {"RDIVF", fdivr},
180 /* POW, RPW deprecated */
181 [8] {"REMF", /*frem*/0},
182 [9] {"FMF", fmul}, /* fast multiply */
183 [10] {"FDV", fdiv}, /* fast divide */
184 [11] {"FRD", fdivr}, /* fast reverse divide */
192 static FP1 voptab1[32] = { /* Vd := OP Vm */
199 static FP2 voptab2[16] = { /* Vd := Vn FOP Fm */
207 fcmp(Internal *n, Internal *m)
212 if(IsWeird(m) || IsWeird(n)){
213 /* BUG: should trap if not masked */
220 i = fpicmp(&rn, &rm);
230 fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
235 (*f)(&FR(ufp, d), mem);
237 print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
241 fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
249 print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea);
254 condok(int cc, int c)
259 case 1: /* Z clear */
263 case 3: /* C clear */
267 case 5: /* N clear */
271 case 7: /* V clear */
273 case 8: /* C set and Z clear */
274 return cc&C && (cc&Z) == 0;
275 case 9: /* C clear or Z set */
276 return (cc&C) == 0 || cc&Z;
277 case 10: /* N set and V set, or N clear and V clear */
278 return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
279 case 11: /* N set and V clear, or N clear and V set */
280 return (cc&(N|V))==N || (cc&(N|V))==V;
281 case 12: /* Z clear, and either N set and V set or N clear and V clear */
282 return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
283 case 13: /* Z set, or N set and V clear or N clear and V set */
284 return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
285 case 14: /* always */
287 case 15: /* never (reserved) */
290 return 0; /* not reached */
294 unimp(ulong pc, ulong op)
298 snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
300 print("FPE: %s\n", buf);
306 fpaemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
311 Internal tmp, *fm, *fn;
313 /* note: would update fault status here if we noted numeric exceptions */
318 if(((op>>25)&7) == 6){
320 unimp(pc, op); /* packed or extended */
323 if((op & (1<<23)) == 0)
333 fld(fpid2i, rd, ea, 8, ufp);
335 fld(fpis2i, rd, ea, 4, ufp);
338 fst(fpii2d, ea, rd, 8, ufp);
340 fst(fpii2s, ea, rd, 4, ufp);
342 if((op & (1<<24)) == 0)
350 * CPRT/transfer, 10.3
358 if(rd == 15 && op & (1<<20)){
373 case 4: /* CMF: Fn :: Fm */
374 case 6: /* CMFE: Fn :: Fm (with exception) */
375 ur->psr &= ~(N|C|Z|V);
376 ur->psr |= fcmp(fn, fm);
378 case 5: /* CNF: Fn :: -Fm */
379 case 7: /* CNFE: Fn :: -Fm (with exception) */
382 ur->psr &= ~(N|C|Z|V);
383 ur->psr |= fcmp(fn, &tmp);
387 print("CMPF %c%d,F%ld =%#lux\n",
388 tag, rn, op&7, ur->psr>>28);
393 * other transfer, 10.3
395 switch((op>>20)&0xF){
400 fpiw2i(&FR(ufp, rn), ®(ur, rd));
402 print("MOVW[FD] R%d, F%d\n", rd, rn);
409 fpii2w(®(ur, rd), &tmp);
411 print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
413 case 2: /* FPSR := Rd */
414 ufp->status = REG(ur, rd);
416 print("MOVW R%d, FPSR\n", rd);
418 case 3: /* Rd := FPSR */
419 REG(ur, rd) = ufp->status;
421 print("MOVW FPSR, R%d\n", rd);
423 case 4: /* FPCR := Rd */
424 ufp->control = REG(ur, rd);
426 print("MOVW R%d, FPCR\n", rd);
428 case 5: /* Rd := FPCR */
429 REG(ur, rd) = ufp->control;
431 print("MOVW FPCR, R%d\n", rd);
441 if(op & (1<<3)){ /* constant */
452 if(op & (1<<15)){ /* monadic */
458 print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd);
459 (*fp->f)(fm, &FR(ufp, rd));
467 print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
468 (*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
473 vfpoptoi(Internal *fm, uchar o2, uchar o4)
476 fm->e = ((o2>>3) | ~(o2 & 4)) - 3 + ExpBias;
478 fm->h = o4 << (20+NGuardBits);
486 vfpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
488 int sz, vd, o1, o2, o3, o4, o, tag;
495 /* note: would update fault status here if we noted numeric exceptions */
502 switch((op>>24) & 0xF){
507 * Extension Register load/store A7.6
510 if((op & (1<<23)) == 0)
512 ea = REG(ur, o2) + off;
513 switch(o1&0x7){ /* D(Bit 22) = 0 (5l) */
518 fst(fpii2d, ea, vd, sz, ufp);
520 fst(fpii2s, ea, vd, sz, ufp);
524 fld(fpid2i, vd, ea, sz, ufp);
526 fld(fpis2i, vd, ea, sz, ufp);
533 * Register transfer between Core & Extension A7.8
535 if(sz) /* C(Bit 8) != 0 */
540 case 0: /* Fn := Rt */
541 *((Word*)&FR(ufp, o2)) = REG(ur, vd);
543 print("MOVWF R%d, F%d\n", vd, o2);
545 case 1: /* Rt := Fn */
546 REG(ur, vd) = *((Word*)&FR(ufp, o2));
548 print("MOVFW F%d, R%d =%ld\n", o2, vd, REG(ur, vd));
550 case 0xE: /* FPSCR := Rt */
551 ufp->status = REG(ur, vd);
553 print("MOVW R%d, FPSCR\n", vd);
555 case 0xF: /* Rt := FPSCR */
557 ur->psr = ufp->status;
559 print("MOVW FPSCR, PSR\n");
561 REG(ur, vd) = ufp->status;
563 print("MOVW FPSCR, R%d\n", vd);
570 * VFP data processing instructions A7.5
571 * Note: As per 5l we ignore (D, N, M) bits
579 if(o1 == 0xB){ /* A7-17 */
583 o = (o2<<1) | (o3>>1);
585 case 0x8: /* CVT int -> float/double */
587 fpiw2i(&FR(ufp, vd), &w);
589 print("CVTW%c F%d, F%d\n", sz?'D':'F', o4, vd);
591 case 0xD: /* CVT float/double -> int */
593 *((Word*)&FR(ufp, vd)) = w;
595 print("CVT%cW F%d, F%d\n", sz?'D':'F', o4, vd);
597 case 0x5: /* CMPF(E) */
601 case 0x4: /* CMPF(E) */
602 ufp->status &= ~(N|C|Z|V);
603 ufp->status |= fcmp(&FR(ufp, vd), fm);
605 print("CMPF %c%d,F%d =%#lux\n",
606 tag, (o2&0x1)? 0: o4, vd, ufp->status>>28);
609 }else{ /* VMOV imm (VFPv3 & v4) (5l doesn't generate) */
610 vfpoptoi(&fc, o2, o4);
621 print("%s %c%d,F%d\n", vfp->name, tag, o4, vd);
622 (*vfp->f)(fm, &FR(ufp, vd));
626 o = ((o1&0x3)<<1) | (o1&0x8) | (o3&0x1);
631 print("%s F%d,F%d,F%d\n", vfp->name, o4, o2, vd);
632 (*vfp->f)(*fm, FR(ufp, o2), &FR(ufp, vd));
640 casemu(ulong pc, ulong op, Ureg *ur)
642 ulong *rp, ro, rn, *rd;
647 ro = rp[op>>16 & 0x7];
648 rn = rp[op>>0 & 0x7];
649 rd = rp + (op>>12 & 0x7);
651 validaddr((uintptr)rp, 4, 1);
653 if(*rd = (*rp == ro))
661 ldrex(ulong pc, ulong op, Ureg *ur)
663 ulong *rp, *rd, *addr;
668 rd = rp + (op>>16 & 0x7);
670 validaddr((uintptr)addr, 4, 0);
672 rp[op>>12 & 0x7] = *addr;
674 print("ldrex, r%ld = [r%ld]@0x%8.8p = 0x%8.8lux",
675 op>>12 & 0x7, op>>16 & 0x7, addr, rp[op>>12 & 0x7]);
679 strex(ulong pc, ulong op, Ureg *ur)
681 ulong *rp, rn, *rd, *addr;
686 rd = rp + (op>>16 & 0x7);
687 rn = rp[op>>0 & 0x7];
689 validaddr((uintptr)addr, 4, 1);
693 print("strex valid, [r%ld]@0x%8.8p = r%ld = 0x%8.8lux",
694 op>>16 & 0x7, addr, op>>0 & 0x7, rn);
697 rp[op>>12 & 0x7] = 0;
700 print("strex invalid, r%ld = 1", op>>16 & 0x7);
701 rp[op>>12 & 0x7] = 1;
709 void (*f)(ulong, ulong, Ureg*);
711 { 0x01900f9f, 0x0ff00fff, ldrex },
712 { 0x01800f90, 0x0ff00ff0, strex },
713 { 0x0ed00100, 0x0ef08100, casemu },
714 { 0x00000000, 0x00000000, nil }
718 * returns the number of FP instructions emulated
728 panic("fpiarm not in a process");
731 * because all the emulated fp state is in the proc structure,
732 * it need not be saved/restored
734 if(up->fpstate != FPactive){
735 // assert(sizeof(Internal) == sizeof(ufp->regs[0]));
736 up->fpstate = FPactive;
738 ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */
739 for(n = 0; n < Nfpctlregs; n++)
740 FR(ufp, n) = fpconst[0];
743 validaddr(ur->pc, 4, 0);
744 op = *(ulong*)(ur->pc);
746 print("%#lux: %#8.8lux ", ur->pc, op);
749 for(i = 0; specialopc[i].f; i++)
750 if((op & specialopc[i].mask) == specialopc[i].opc)
753 if(condok(ur->psr, op>>28))
754 specialopc[i].f(ur->pc, op, ur);
756 else if(ISVFPOP(cp, o)){
757 if(condok(ur->psr, op>>28))
758 vfpemu(ur->pc, op, ur, ufp);
760 else if(ISFPAOP(cp, o)){
761 if(condok(ur->psr, op>>28))
762 fpaemu(ur->pc, op, ur, ufp);
767 ur->pc += 4; /* pretend cpu executed the instr */