ARM: kernel: add vfp emulator to bcm, teg2, omap, kw

This commit is contained in:
jpathy 2013-06-12 01:11:41 +05:30
parent f166778ce3
commit 9f949e3cd3
8 changed files with 844 additions and 64 deletions

View file

@ -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)

View file

@ -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 */

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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)