From 9f949e3cd311efe97673bf997405257dbb227888 Mon Sep 17 00:00:00 2001 From: jpathy Date: Wed, 12 Jun 2013 01:11:41 +0530 Subject: [PATCH] ARM: kernel: add vfp emulator to bcm, teg2, omap, kw --- sys/src/9/bcm/fpiarm.c | 207 +++++++++++++++++++++++++++++++++-- sys/src/9/kw/arm.h | 6 + sys/src/9/kw/dat.h | 6 +- sys/src/9/kw/fpiarm.c | 235 ++++++++++++++++++++++++++++++++++++---- sys/src/9/omap/arm.h | 6 + sys/src/9/omap/dat.h | 6 +- sys/src/9/omap/fpiarm.c | 235 ++++++++++++++++++++++++++++++++++++---- sys/src/9/teg2/fpiarm.c | 207 +++++++++++++++++++++++++++++++++-- 8 files changed, 844 insertions(+), 64 deletions(-) diff --git a/sys/src/9/bcm/fpiarm.c b/sys/src/9/bcm/fpiarm.c index 571e89fa6..d92455328 100644 --- a/sys/src/9/bcm/fpiarm.c +++ b/sys/src/9/bcm/fpiarm.c @@ -15,18 +15,11 @@ #include "arm.h" #include "fpi.h" -#define ARM7500 /* emulate old pre-VFP opcodes */ - /* undef this if correct kernel r13 isn't in Ureg; * check calculation in fpiarm below */ - #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)])) -#ifdef ARM7500 -#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7]) -#else -#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)]) -#endif +#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpctlregs - 1)]) typedef struct FP2 FP2; typedef struct FP1 FP1; @@ -192,6 +185,24 @@ static FP2 optab2[16] = { /* Fd := Fn OP Fm */ /* POL deprecated */ }; +/* + * ARM VFP opcodes + */ + +static FP1 voptab1[32] = { /* Vd := OP Vm */ +[0] {"MOVF", fmov}, +[1] {"ABSF", fabsf}, +[2] {"NEGF", fmovn}, +[15] {"CVTF", fmov}, +}; + +static FP2 voptab2[16] = { /* Vd := Vn FOP Fm */ +[4] {"MULF", fmul}, +[6] {"ADDF", fadd}, +[7] {"SUBF", fsub}, +[8] {"DIVF", fdiv}, +}; + static ulong fcmp(Internal *n, Internal *m) { @@ -292,7 +303,7 @@ unimp(ulong pc, ulong op) } static void -fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +fpaemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) { int rn, rd, tag, o; long off; @@ -458,6 +469,174 @@ fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) } } +static void +vfpoptoi(Internal *fm, uchar o2, uchar o4) +{ + fm->s = o2>>3; + fm->e = ((o2>>3) | ~(o2 & 4)) - 3 + ExpBias; + fm->l = 0; + fm->h = o4 << (20+NGuardBits); + if(fm->e) + fm->h |= HiddenBit; + else + fm->e++; +} + +static void +vfpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +{ + int sz, vd, o1, o2, o3, o4, o, tag; + long off; + ulong ea; + Word w; + + Internal *fm, fc; + + /* note: would update fault status here if we noted numeric exceptions */ + + sz = op & (1<<8); + o1 = (op>>20) & 0xF; + o2 = (op>>16) & 0xF; + vd = (op>>12) & 0xF; + + switch((op>>24) & 0xF){ + default: + unimp(pc, op); + case 0xD: + /* + * Extension Register load/store A7.6 + */ + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(ur, o2) + off; + switch(o1&0x7){ /* D(Bit 22) = 0 (5l) */ + default: + unimp(pc, op); + case 0: + if(sz) + fst(fpii2d, ea, vd, sz, ufp); + else + fst(fpii2s, ea, vd, sz, ufp); + break; + case 1: + if(sz) + fld(fpid2i, vd, ea, sz, ufp); + else + fld(fpis2i, vd, ea, sz, ufp); + break; + } + break; + case 0xE: + if(op & (1<<4)){ + /* + * Register transfer between Core & Extension A7.8 + */ + if(sz) /* C(Bit 8) != 0 */ + unimp(pc, op); + switch(o1){ + default: + unimp(pc, op); + case 0: /* Fn := Rt */ + *((Word*)&FR(ufp, o2)) = REG(ur, vd); + if(fpemudebug) + print("MOVWF R%d, F%d\n", vd, o2); + break; + case 1: /* Rt := Fn */ + REG(ur, vd) = *((Word*)&FR(ufp, o2)); + if(fpemudebug) + print("MOVFW F%d, R%d =%ld\n", o2, vd, REG(ur, vd)); + break; + case 0xE: /* FPSCR := Rt */ + ufp->status = REG(ur, vd); + if(fpemudebug) + print("MOVW R%d, FPSCR\n", vd); + break; + case 0xF: /* Rt := FPSCR */ + if(vd == 0xF){ + ur->psr = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, PSR\n"); + }else{ + REG(ur, vd) = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, R%d\n", vd); + } + break; + } + } + else{ + /* + * VFP data processing instructions A7.5 + * Note: As per 5l we ignore (D, N, M) bits + */ + if(fpemudebug) + tag = 'F'; + o3 = (op>>6) & 0x3; + o4 = op & 0xF; + fm = &FR(ufp, o4); + + if(o1 == 0xB){ /* A7-17 */ + if(o3 & 0x1){ + switch(o2){ + default: + o = (o2<<1) | (o3>>1); + break; + case 0x8: /* CVT int -> float/double */ + w = *((Word*)fm); + fpiw2i(&FR(ufp, vd), &w); + if(fpemudebug) + print("CVTW%c F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0xD: /* CVT float/double -> int */ + fpii2w(&w, fm); + *((Word*)&FR(ufp, vd)) = w; + if(fpemudebug) + print("CVT%cW F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0x5: /* CMPF(E) */ + fm = &fpconst[0]; + if(fpemudebug) + tag = 'C'; + case 0x4: /* CMPF(E) */ + ufp->status &= ~(N|C|Z|V); + ufp->status |= fcmp(&FR(ufp, vd), fm); + if(fpemudebug) + print("CMPF %c%d,F%d =%#lux\n", + tag, (o2&0x1)? 0: o4, vd, ufp->status>>28); + return; + } + }else{ /* VMOV imm (VFPv3 & v4) (5l doesn't generate) */ + vfpoptoi(&fc, o2, o4); + fm = &fc; + o = 0; + if(fpemudebug) + tag = 'C'; + } + FP1 *vfp; + vfp = &voptab1[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%d,F%d\n", vfp->name, tag, o4, vd); + (*vfp->f)(fm, &FR(ufp, vd)); + } + else { /* A7-16 */ + FP2 *vfp; + o = ((o1&0x3)<<1) | (o1&0x8) | (o3&0x1); + vfp = &voptab2[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s F%d,F%d,F%d\n", vfp->name, o4, o2, vd); + (*vfp->f)(*fm, FR(ufp, o2), &FR(ufp, vd)); + } + } + break; + } +} + + /* * returns the number of FP instructions emulated */ @@ -467,6 +646,7 @@ fpiarm(Ureg *ur) ulong op, o, cp; FPsave *ufp; int n; + void (*fpemu)(ulong , ulong , Ureg *, FPsave *); if(up == nil) panic("fpiarm not in a process"); @@ -484,7 +664,7 @@ fpiarm(Ureg *ur) up->fpstate = FPemu; ufp->control = 0; ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */ - for(n = 0; n < 8; n++) + for(n = 0; n < Nfpctlregs; n++) FR(ufp, n) = fpconst[0]; } for(n=0; ;n++){ @@ -494,10 +674,15 @@ fpiarm(Ureg *ur) print("%#lux: %#8.8lux ", ur->pc, op); o = (op>>24) & 0xF; cp = (op>>8) & 0xF; - if(!ISFPAOP(cp, o)) + if(ISFPAOP(cp, o)) + fpemu = fpaemu; + else if(ISVFPOP(cp, o)) + fpemu = vfpemu; + else break; if(condok(ur->psr, op>>28)) fpemu(ur->pc, op, ur, ufp); + ur->pc += 4; /* pretend cpu executed the instr */ } if(fpemudebug) diff --git a/sys/src/9/kw/arm.h b/sys/src/9/kw/arm.h index a9e2ff100..5b6fc2767 100644 --- a/sys/src/9/kw/arm.h +++ b/sys/src/9/kw/arm.h @@ -18,9 +18,15 @@ #define PsrZ 0x40000000 /* zero */ #define PsrN 0x80000000 /* negative/less than */ +/* instruction decoding */ +#define ISCPOP(op) ((op) == 0xE || ((op) & ~1) == 0xC) +#define ISFPAOP(cp, op) ((cp) == CpOFPA && ISCPOP(op)) +#define ISVFPOP(cp, op) (((cp) == CpDFP || (cp) == CpFP) && ISCPOP(op)) + /* * Coprocessors */ +#define CpOFPA 1 /* ancient 7500 FPA */ #define CpFP 10 /* float FP, VFP cfg. */ #define CpDFP 11 /* double FP */ #define CpSC 15 /* System Control */ diff --git a/sys/src/9/kw/dat.h b/sys/src/9/kw/dat.h index 88b901cf6..f0327bb6a 100644 --- a/sys/src/9/kw/dat.h +++ b/sys/src/9/kw/dat.h @@ -45,6 +45,10 @@ struct Label uintptr pc; }; +enum{ + Nfpctlregs = 16, +}; + /* * emulated floating point */ @@ -52,7 +56,7 @@ struct FPsave { ulong status; ulong control; - ulong regs[8][3]; + ulong regs[Nfpctlregs][3]; int fpstate; }; diff --git a/sys/src/9/kw/fpiarm.c b/sys/src/9/kw/fpiarm.c index 063d10c52..99611e0cd 100644 --- a/sys/src/9/kw/fpiarm.c +++ b/sys/src/9/kw/fpiarm.c @@ -18,10 +18,8 @@ /* undef this if correct kernel r13 isn't in Ureg; * check calculation in fpiarm below */ - - #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)])) -#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7]) +#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpctlregs - 1)]) typedef struct FP2 FP2; typedef struct FP1 FP1; @@ -58,7 +56,7 @@ static int roff[] = { OFR(r12), OFR(r13), OFR(r14), OFR(pc), }; -static Internal fpconst[8] = { /* indexed by op&7 */ +static Internal fpconst[8] = { /* indexed by op&7 (ARM 7500 FPA) */ /* s, e, l, h */ {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ @@ -158,6 +156,10 @@ frnd(Internal *m, Internal *d) } } +/* + * ARM 7500 FPA opcodes + */ + static FP1 optab1[16] = { /* Fd := OP Fm */ [0] {"MOVF", fmov}, [1] {"NEGF", fmovn}, @@ -183,6 +185,24 @@ static FP2 optab2[16] = { /* Fd := Fn OP Fm */ /* POL deprecated */ }; +/* + * ARM VFP opcodes + */ + +static FP1 voptab1[32] = { /* Vd := OP Vm */ +[0] {"MOVF", fmov}, +[1] {"ABSF", fabsf}, +[2] {"NEGF", fmovn}, +[15] {"CVTF", fmov}, +}; + +static FP2 voptab2[16] = { /* Vd := Vn FOP Fm */ +[4] {"MULF", fmul}, +[6] {"ADDF", fadd}, +[7] {"SUBF", fsub}, +[8] {"DIVF", fdiv}, +}; + static ulong fcmp(Internal *n, Internal *m) { @@ -283,7 +303,7 @@ unimp(ulong pc, ulong op) } static void -fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +fpaemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) { int rn, rd, tag, o; long off; @@ -449,6 +469,173 @@ fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) } } +static void +vfpoptoi(Internal *fm, uchar o2, uchar o4) +{ + fm->s = o2>>3; + fm->e = ((o2>>3) | ~(o2 & 4)) - 3 + ExpBias; + fm->l = 0; + fm->h = o4 << (20+NGuardBits); + if(fm->e) + fm->h |= HiddenBit; + else + fm->e++; +} + +static void +vfpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +{ + int sz, vd, o1, o2, o3, o4, o, tag; + long off; + ulong ea; + Word w; + + Internal *fm, fc; + + /* note: would update fault status here if we noted numeric exceptions */ + + sz = op & (1<<8); + o1 = (op>>20) & 0xF; + o2 = (op>>16) & 0xF; + vd = (op>>12) & 0xF; + + switch((op>>24) & 0xF){ + default: + unimp(pc, op); + case 0xD: + /* + * Extension Register load/store A7.6 + */ + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(ur, o2) + off; + switch(o1&0x7){ /* D(Bit 22) = 0 (5l) */ + default: + unimp(pc, op); + case 0: + if(sz) + fst(fpii2d, ea, vd, sz, ufp); + else + fst(fpii2s, ea, vd, sz, ufp); + break; + case 1: + if(sz) + fld(fpid2i, vd, ea, sz, ufp); + else + fld(fpis2i, vd, ea, sz, ufp); + break; + } + break; + case 0xE: + if(op & (1<<4)){ + /* + * Register transfer between Core & Extension A7.8 + */ + if(sz) /* C(Bit 8) != 0 */ + unimp(pc, op); + switch(o1){ + default: + unimp(pc, op); + case 0: /* Fn := Rt */ + *((Word*)&FR(ufp, o2)) = REG(ur, vd); + if(fpemudebug) + print("MOVWF R%d, F%d\n", vd, o2); + break; + case 1: /* Rt := Fn */ + REG(ur, vd) = *((Word*)&FR(ufp, o2)); + if(fpemudebug) + print("MOVFW F%d, R%d =%ld\n", o2, vd, REG(ur, vd)); + break; + case 0xE: /* FPSCR := Rt */ + ufp->status = REG(ur, vd); + if(fpemudebug) + print("MOVW R%d, FPSCR\n", vd); + break; + case 0xF: /* Rt := FPSCR */ + if(vd == 0xF){ + ur->psr = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, PSR\n"); + }else{ + REG(ur, vd) = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, R%d\n", vd); + } + break; + } + } + else{ + /* + * VFP data processing instructions A7.5 + * Note: As per 5l we ignore (D, N, M) bits + */ + if(fpemudebug) + tag = 'F'; + o3 = (op>>6) & 0x3; + o4 = op & 0xF; + fm = &FR(ufp, o4); + + if(o1 == 0xB){ /* A7-17 */ + if(o3 & 0x1){ + switch(o2){ + default: + o = (o2<<1) | (o3>>1); + break; + case 0x8: /* CVT int -> float/double */ + w = *((Word*)fm); + fpiw2i(&FR(ufp, vd), &w); + if(fpemudebug) + print("CVTW%c F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0xD: /* CVT float/double -> int */ + fpii2w(&w, fm); + *((Word*)&FR(ufp, vd)) = w; + if(fpemudebug) + print("CVT%cW F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0x5: /* CMPF(E) */ + fm = &fpconst[0]; + if(fpemudebug) + tag = 'C'; + case 0x4: /* CMPF(E) */ + ufp->status &= ~(N|C|Z|V); + ufp->status |= fcmp(&FR(ufp, vd), fm); + if(fpemudebug) + print("CMPF %c%d,F%d =%#lux\n", + tag, (o2&0x1)? 0: o4, vd, ufp->status>>28); + return; + } + }else{ /* VMOV imm (VFPv3 & v4) (5l doesn't generate) */ + vfpoptoi(&fc, o2, o4); + fm = &fc; + o = 0; + if(fpemudebug) + tag = 'C'; + } + FP1 *vfp; + vfp = &voptab1[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%d,F%d\n", vfp->name, tag, o4, vd); + (*vfp->f)(fm, &FR(ufp, vd)); + } + else { /* A7-16 */ + FP2 *vfp; + o = ((o1&0x3)<<1) | (o1&0x8) | (o3&0x1); + vfp = &voptab2[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s F%d,F%d,F%d\n", vfp->name, o4, o2, vd); + (*vfp->f)(*fm, FR(ufp, o2), &FR(ufp, vd)); + } + } + break; + } +} + void casemu(ulong pc, ulong op, Ureg *ur) { @@ -533,14 +720,16 @@ struct { int fpiarm(Ureg *ur) { - ulong op, o; + ulong op, o, cp; FPsave *ufp; int i, n; + void (*fpemu)(ulong , ulong , Ureg *, FPsave *); if(up == nil) panic("fpiarm not in a process"); ufp = &up->fpsave; - /* because all the state is in the proc structure, + /* + * because all the emulated fp state is in the proc structure, * it need not be saved/restored */ if(up->fpstate != FPactive){ @@ -548,7 +737,7 @@ fpiarm(Ureg *ur) up->fpstate = FPactive; ufp->control = 0; ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ - for(n = 0; n < 8; n++) + for(n = 0; n < Nfpctlregs; n++) FR(ufp, n) = fpconst[0]; } for(n=0; ;n++){ @@ -557,20 +746,26 @@ fpiarm(Ureg *ur) if(fpemudebug) print("%#lux: %#8.8lux ", ur->pc, op); o = (op>>24) & 0xF; - if(condok(ur->psr, op>>28)){ - for(i = 0; specialopc[i].f; i++) - if((op & specialopc[i].mask) == specialopc[i].opc) - break; - if(specialopc[i].f) - specialopc[i].f(ur->pc, op, ur); - else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) + cp = (op>>8) & 0xF; + for(i = 0; specialopc[i].f; i++) + if((op & specialopc[i].mask) == specialopc[i].opc) break; - else - fpemu(ur->pc, op, ur, ufp); - }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) + if(specialopc[i].f) + specialopc[i].f(ur->pc, op, ur); + else if(ISVFPOP(cp, o)){ + if(condok(ur->psr, op>>28)) + vfpemu(ur->pc, op, ur, ufp); + } + else if(ISFPAOP(cp, o)){ + if(condok(ur->psr, op>>28)) + fpaemu(ur->pc, op, ur, ufp); + } + else break; - ur->pc += 4; + + ur->pc += 4; /* pretend cpu executed the instr */ } - if(fpemudebug) print("\n"); + if(fpemudebug) + print("\n"); return n; } diff --git a/sys/src/9/omap/arm.h b/sys/src/9/omap/arm.h index 0376f36fb..250a0ba42 100644 --- a/sys/src/9/omap/arm.h +++ b/sys/src/9/omap/arm.h @@ -26,9 +26,15 @@ #define PsrZ 0x40000000 /* zero */ #define PsrN 0x80000000 /* negative/less than */ +/* instruction decoding */ +#define ISCPOP(op) ((op) == 0xE || ((op) & ~1) == 0xC) +#define ISFPAOP(cp, op) ((cp) == CpOFPA && ISCPOP(op)) +#define ISVFPOP(cp, op) (((cp) == CpDFP || (cp) == CpFP) && ISCPOP(op)) + /* * Coprocessors */ +#define CpOFPA 1 /* ancient 7500 FPA */ #define CpFP 10 /* float FP, VFP cfg. */ #define CpDFP 11 /* double FP */ #define CpSC 15 /* System Control */ diff --git a/sys/src/9/omap/dat.h b/sys/src/9/omap/dat.h index 37114691d..6ed388c15 100644 --- a/sys/src/9/omap/dat.h +++ b/sys/src/9/omap/dat.h @@ -69,6 +69,10 @@ struct Label uintptr pc; }; +enum{ + Nfpctlregs = 16, +}; + /* * emulated floating point */ @@ -76,7 +80,7 @@ struct FPsave { ulong status; ulong control; - ulong regs[8][3]; + ulong regs[Nfpctlregs][3]; int fpstate; }; diff --git a/sys/src/9/omap/fpiarm.c b/sys/src/9/omap/fpiarm.c index 063d10c52..99611e0cd 100644 --- a/sys/src/9/omap/fpiarm.c +++ b/sys/src/9/omap/fpiarm.c @@ -18,10 +18,8 @@ /* undef this if correct kernel r13 isn't in Ureg; * check calculation in fpiarm below */ - - #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)])) -#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7]) +#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpctlregs - 1)]) typedef struct FP2 FP2; typedef struct FP1 FP1; @@ -58,7 +56,7 @@ static int roff[] = { OFR(r12), OFR(r13), OFR(r14), OFR(pc), }; -static Internal fpconst[8] = { /* indexed by op&7 */ +static Internal fpconst[8] = { /* indexed by op&7 (ARM 7500 FPA) */ /* s, e, l, h */ {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ @@ -158,6 +156,10 @@ frnd(Internal *m, Internal *d) } } +/* + * ARM 7500 FPA opcodes + */ + static FP1 optab1[16] = { /* Fd := OP Fm */ [0] {"MOVF", fmov}, [1] {"NEGF", fmovn}, @@ -183,6 +185,24 @@ static FP2 optab2[16] = { /* Fd := Fn OP Fm */ /* POL deprecated */ }; +/* + * ARM VFP opcodes + */ + +static FP1 voptab1[32] = { /* Vd := OP Vm */ +[0] {"MOVF", fmov}, +[1] {"ABSF", fabsf}, +[2] {"NEGF", fmovn}, +[15] {"CVTF", fmov}, +}; + +static FP2 voptab2[16] = { /* Vd := Vn FOP Fm */ +[4] {"MULF", fmul}, +[6] {"ADDF", fadd}, +[7] {"SUBF", fsub}, +[8] {"DIVF", fdiv}, +}; + static ulong fcmp(Internal *n, Internal *m) { @@ -283,7 +303,7 @@ unimp(ulong pc, ulong op) } static void -fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +fpaemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) { int rn, rd, tag, o; long off; @@ -449,6 +469,173 @@ fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) } } +static void +vfpoptoi(Internal *fm, uchar o2, uchar o4) +{ + fm->s = o2>>3; + fm->e = ((o2>>3) | ~(o2 & 4)) - 3 + ExpBias; + fm->l = 0; + fm->h = o4 << (20+NGuardBits); + if(fm->e) + fm->h |= HiddenBit; + else + fm->e++; +} + +static void +vfpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +{ + int sz, vd, o1, o2, o3, o4, o, tag; + long off; + ulong ea; + Word w; + + Internal *fm, fc; + + /* note: would update fault status here if we noted numeric exceptions */ + + sz = op & (1<<8); + o1 = (op>>20) & 0xF; + o2 = (op>>16) & 0xF; + vd = (op>>12) & 0xF; + + switch((op>>24) & 0xF){ + default: + unimp(pc, op); + case 0xD: + /* + * Extension Register load/store A7.6 + */ + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(ur, o2) + off; + switch(o1&0x7){ /* D(Bit 22) = 0 (5l) */ + default: + unimp(pc, op); + case 0: + if(sz) + fst(fpii2d, ea, vd, sz, ufp); + else + fst(fpii2s, ea, vd, sz, ufp); + break; + case 1: + if(sz) + fld(fpid2i, vd, ea, sz, ufp); + else + fld(fpis2i, vd, ea, sz, ufp); + break; + } + break; + case 0xE: + if(op & (1<<4)){ + /* + * Register transfer between Core & Extension A7.8 + */ + if(sz) /* C(Bit 8) != 0 */ + unimp(pc, op); + switch(o1){ + default: + unimp(pc, op); + case 0: /* Fn := Rt */ + *((Word*)&FR(ufp, o2)) = REG(ur, vd); + if(fpemudebug) + print("MOVWF R%d, F%d\n", vd, o2); + break; + case 1: /* Rt := Fn */ + REG(ur, vd) = *((Word*)&FR(ufp, o2)); + if(fpemudebug) + print("MOVFW F%d, R%d =%ld\n", o2, vd, REG(ur, vd)); + break; + case 0xE: /* FPSCR := Rt */ + ufp->status = REG(ur, vd); + if(fpemudebug) + print("MOVW R%d, FPSCR\n", vd); + break; + case 0xF: /* Rt := FPSCR */ + if(vd == 0xF){ + ur->psr = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, PSR\n"); + }else{ + REG(ur, vd) = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, R%d\n", vd); + } + break; + } + } + else{ + /* + * VFP data processing instructions A7.5 + * Note: As per 5l we ignore (D, N, M) bits + */ + if(fpemudebug) + tag = 'F'; + o3 = (op>>6) & 0x3; + o4 = op & 0xF; + fm = &FR(ufp, o4); + + if(o1 == 0xB){ /* A7-17 */ + if(o3 & 0x1){ + switch(o2){ + default: + o = (o2<<1) | (o3>>1); + break; + case 0x8: /* CVT int -> float/double */ + w = *((Word*)fm); + fpiw2i(&FR(ufp, vd), &w); + if(fpemudebug) + print("CVTW%c F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0xD: /* CVT float/double -> int */ + fpii2w(&w, fm); + *((Word*)&FR(ufp, vd)) = w; + if(fpemudebug) + print("CVT%cW F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0x5: /* CMPF(E) */ + fm = &fpconst[0]; + if(fpemudebug) + tag = 'C'; + case 0x4: /* CMPF(E) */ + ufp->status &= ~(N|C|Z|V); + ufp->status |= fcmp(&FR(ufp, vd), fm); + if(fpemudebug) + print("CMPF %c%d,F%d =%#lux\n", + tag, (o2&0x1)? 0: o4, vd, ufp->status>>28); + return; + } + }else{ /* VMOV imm (VFPv3 & v4) (5l doesn't generate) */ + vfpoptoi(&fc, o2, o4); + fm = &fc; + o = 0; + if(fpemudebug) + tag = 'C'; + } + FP1 *vfp; + vfp = &voptab1[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%d,F%d\n", vfp->name, tag, o4, vd); + (*vfp->f)(fm, &FR(ufp, vd)); + } + else { /* A7-16 */ + FP2 *vfp; + o = ((o1&0x3)<<1) | (o1&0x8) | (o3&0x1); + vfp = &voptab2[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s F%d,F%d,F%d\n", vfp->name, o4, o2, vd); + (*vfp->f)(*fm, FR(ufp, o2), &FR(ufp, vd)); + } + } + break; + } +} + void casemu(ulong pc, ulong op, Ureg *ur) { @@ -533,14 +720,16 @@ struct { int fpiarm(Ureg *ur) { - ulong op, o; + ulong op, o, cp; FPsave *ufp; int i, n; + void (*fpemu)(ulong , ulong , Ureg *, FPsave *); if(up == nil) panic("fpiarm not in a process"); ufp = &up->fpsave; - /* because all the state is in the proc structure, + /* + * because all the emulated fp state is in the proc structure, * it need not be saved/restored */ if(up->fpstate != FPactive){ @@ -548,7 +737,7 @@ fpiarm(Ureg *ur) up->fpstate = FPactive; ufp->control = 0; ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */ - for(n = 0; n < 8; n++) + for(n = 0; n < Nfpctlregs; n++) FR(ufp, n) = fpconst[0]; } for(n=0; ;n++){ @@ -557,20 +746,26 @@ fpiarm(Ureg *ur) if(fpemudebug) print("%#lux: %#8.8lux ", ur->pc, op); o = (op>>24) & 0xF; - if(condok(ur->psr, op>>28)){ - for(i = 0; specialopc[i].f; i++) - if((op & specialopc[i].mask) == specialopc[i].opc) - break; - if(specialopc[i].f) - specialopc[i].f(ur->pc, op, ur); - else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) + cp = (op>>8) & 0xF; + for(i = 0; specialopc[i].f; i++) + if((op & specialopc[i].mask) == specialopc[i].opc) break; - else - fpemu(ur->pc, op, ur, ufp); - }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC) + if(specialopc[i].f) + specialopc[i].f(ur->pc, op, ur); + else if(ISVFPOP(cp, o)){ + if(condok(ur->psr, op>>28)) + vfpemu(ur->pc, op, ur, ufp); + } + else if(ISFPAOP(cp, o)){ + if(condok(ur->psr, op>>28)) + fpaemu(ur->pc, op, ur, ufp); + } + else break; - ur->pc += 4; + + ur->pc += 4; /* pretend cpu executed the instr */ } - if(fpemudebug) print("\n"); + if(fpemudebug) + print("\n"); return n; } diff --git a/sys/src/9/teg2/fpiarm.c b/sys/src/9/teg2/fpiarm.c index 571e89fa6..d92455328 100644 --- a/sys/src/9/teg2/fpiarm.c +++ b/sys/src/9/teg2/fpiarm.c @@ -15,18 +15,11 @@ #include "arm.h" #include "fpi.h" -#define ARM7500 /* emulate old pre-VFP opcodes */ - /* undef this if correct kernel r13 isn't in Ureg; * check calculation in fpiarm below */ - #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)])) -#ifdef ARM7500 -#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7]) -#else -#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)]) -#endif +#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpctlregs - 1)]) typedef struct FP2 FP2; typedef struct FP1 FP1; @@ -192,6 +185,24 @@ static FP2 optab2[16] = { /* Fd := Fn OP Fm */ /* POL deprecated */ }; +/* + * ARM VFP opcodes + */ + +static FP1 voptab1[32] = { /* Vd := OP Vm */ +[0] {"MOVF", fmov}, +[1] {"ABSF", fabsf}, +[2] {"NEGF", fmovn}, +[15] {"CVTF", fmov}, +}; + +static FP2 voptab2[16] = { /* Vd := Vn FOP Fm */ +[4] {"MULF", fmul}, +[6] {"ADDF", fadd}, +[7] {"SUBF", fsub}, +[8] {"DIVF", fdiv}, +}; + static ulong fcmp(Internal *n, Internal *m) { @@ -292,7 +303,7 @@ unimp(ulong pc, ulong op) } static void -fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +fpaemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) { int rn, rd, tag, o; long off; @@ -458,6 +469,174 @@ fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) } } +static void +vfpoptoi(Internal *fm, uchar o2, uchar o4) +{ + fm->s = o2>>3; + fm->e = ((o2>>3) | ~(o2 & 4)) - 3 + ExpBias; + fm->l = 0; + fm->h = o4 << (20+NGuardBits); + if(fm->e) + fm->h |= HiddenBit; + else + fm->e++; +} + +static void +vfpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp) +{ + int sz, vd, o1, o2, o3, o4, o, tag; + long off; + ulong ea; + Word w; + + Internal *fm, fc; + + /* note: would update fault status here if we noted numeric exceptions */ + + sz = op & (1<<8); + o1 = (op>>20) & 0xF; + o2 = (op>>16) & 0xF; + vd = (op>>12) & 0xF; + + switch((op>>24) & 0xF){ + default: + unimp(pc, op); + case 0xD: + /* + * Extension Register load/store A7.6 + */ + off = (op&0xFF)<<2; + if((op & (1<<23)) == 0) + off = -off; + ea = REG(ur, o2) + off; + switch(o1&0x7){ /* D(Bit 22) = 0 (5l) */ + default: + unimp(pc, op); + case 0: + if(sz) + fst(fpii2d, ea, vd, sz, ufp); + else + fst(fpii2s, ea, vd, sz, ufp); + break; + case 1: + if(sz) + fld(fpid2i, vd, ea, sz, ufp); + else + fld(fpis2i, vd, ea, sz, ufp); + break; + } + break; + case 0xE: + if(op & (1<<4)){ + /* + * Register transfer between Core & Extension A7.8 + */ + if(sz) /* C(Bit 8) != 0 */ + unimp(pc, op); + switch(o1){ + default: + unimp(pc, op); + case 0: /* Fn := Rt */ + *((Word*)&FR(ufp, o2)) = REG(ur, vd); + if(fpemudebug) + print("MOVWF R%d, F%d\n", vd, o2); + break; + case 1: /* Rt := Fn */ + REG(ur, vd) = *((Word*)&FR(ufp, o2)); + if(fpemudebug) + print("MOVFW F%d, R%d =%ld\n", o2, vd, REG(ur, vd)); + break; + case 0xE: /* FPSCR := Rt */ + ufp->status = REG(ur, vd); + if(fpemudebug) + print("MOVW R%d, FPSCR\n", vd); + break; + case 0xF: /* Rt := FPSCR */ + if(vd == 0xF){ + ur->psr = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, PSR\n"); + }else{ + REG(ur, vd) = ufp->status; + if(fpemudebug) + print("MOVW FPSCR, R%d\n", vd); + } + break; + } + } + else{ + /* + * VFP data processing instructions A7.5 + * Note: As per 5l we ignore (D, N, M) bits + */ + if(fpemudebug) + tag = 'F'; + o3 = (op>>6) & 0x3; + o4 = op & 0xF; + fm = &FR(ufp, o4); + + if(o1 == 0xB){ /* A7-17 */ + if(o3 & 0x1){ + switch(o2){ + default: + o = (o2<<1) | (o3>>1); + break; + case 0x8: /* CVT int -> float/double */ + w = *((Word*)fm); + fpiw2i(&FR(ufp, vd), &w); + if(fpemudebug) + print("CVTW%c F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0xD: /* CVT float/double -> int */ + fpii2w(&w, fm); + *((Word*)&FR(ufp, vd)) = w; + if(fpemudebug) + print("CVT%cW F%d, F%d\n", sz?'D':'F', o4, vd); + return; + case 0x5: /* CMPF(E) */ + fm = &fpconst[0]; + if(fpemudebug) + tag = 'C'; + case 0x4: /* CMPF(E) */ + ufp->status &= ~(N|C|Z|V); + ufp->status |= fcmp(&FR(ufp, vd), fm); + if(fpemudebug) + print("CMPF %c%d,F%d =%#lux\n", + tag, (o2&0x1)? 0: o4, vd, ufp->status>>28); + return; + } + }else{ /* VMOV imm (VFPv3 & v4) (5l doesn't generate) */ + vfpoptoi(&fc, o2, o4); + fm = &fc; + o = 0; + if(fpemudebug) + tag = 'C'; + } + FP1 *vfp; + vfp = &voptab1[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s %c%d,F%d\n", vfp->name, tag, o4, vd); + (*vfp->f)(fm, &FR(ufp, vd)); + } + else { /* A7-16 */ + FP2 *vfp; + o = ((o1&0x3)<<1) | (o1&0x8) | (o3&0x1); + vfp = &voptab2[o]; + if(vfp->f == nil) + unimp(pc, op); + if(fpemudebug) + print("%s F%d,F%d,F%d\n", vfp->name, o4, o2, vd); + (*vfp->f)(*fm, FR(ufp, o2), &FR(ufp, vd)); + } + } + break; + } +} + + /* * returns the number of FP instructions emulated */ @@ -467,6 +646,7 @@ fpiarm(Ureg *ur) ulong op, o, cp; FPsave *ufp; int n; + void (*fpemu)(ulong , ulong , Ureg *, FPsave *); if(up == nil) panic("fpiarm not in a process"); @@ -484,7 +664,7 @@ fpiarm(Ureg *ur) up->fpstate = FPemu; ufp->control = 0; ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */ - for(n = 0; n < 8; n++) + for(n = 0; n < Nfpctlregs; n++) FR(ufp, n) = fpconst[0]; } for(n=0; ;n++){ @@ -494,10 +674,15 @@ fpiarm(Ureg *ur) print("%#lux: %#8.8lux ", ur->pc, op); o = (op>>24) & 0xF; cp = (op>>8) & 0xF; - if(!ISFPAOP(cp, o)) + if(ISFPAOP(cp, o)) + fpemu = fpaemu; + else if(ISVFPOP(cp, o)) + fpemu = vfpemu; + else break; if(condok(ur->psr, op>>28)) fpemu(ur->pc, op, ur, ufp); + ur->pc += 4; /* pretend cpu executed the instr */ } if(fpemudebug)