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