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 /* undef this if correct kernel r13 isn't in Ureg;
19 * check calculation in fpiarm below
20 */
21
22
23 #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
24 #define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
25
26 typedef struct FP2 FP2;
27 typedef struct FP1 FP1;
28
29 struct FP2 {
30 char* name;
31 void (*f)(Internal, Internal, Internal*);
32 };
33
34 struct FP1 {
35 char* name;
36 void (*f)(Internal*, Internal*);
37 };
38
39 enum {
40 N = 1<<31,
41 Z = 1<<30,
42 C = 1<<29,
43 V = 1<<28,
44 REGPC = 15,
45 };
46
47 enum {
48 fpemudebug = 0,
49 };
50
51 #undef OFR
52 #define OFR(X) ((ulong)&((Ureg*)0)->X)
53
54 static int roff[] = {
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),
59 };
60
61 static Internal fpconst[8] = { /* indexed by op&7 */
62 /* s, e, l, h */
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 */
71 };
72
73 /*
74 * arm binary operations
75 */
76
77 static void
fadd(Internal m,Internal n,Internal * d)78 fadd(Internal m, Internal n, Internal *d)
79 {
80 (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
81 }
82
83 static void
fsub(Internal m,Internal n,Internal * d)84 fsub(Internal m, Internal n, Internal *d)
85 {
86 m.s ^= 1;
87 (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
88 }
89
90 static void
fsubr(Internal m,Internal n,Internal * d)91 fsubr(Internal m, Internal n, Internal *d)
92 {
93 n.s ^= 1;
94 (n.s == m.s? fpiadd: fpisub)(&n, &m, d);
95 }
96
97 static void
fmul(Internal m,Internal n,Internal * d)98 fmul(Internal m, Internal n, Internal *d)
99 {
100 fpimul(&m, &n, d);
101 }
102
103 static void
fdiv(Internal m,Internal n,Internal * d)104 fdiv(Internal m, Internal n, Internal *d)
105 {
106 fpidiv(&m, &n, d);
107 }
108
109 static void
fdivr(Internal m,Internal n,Internal * d)110 fdivr(Internal m, Internal n, Internal *d)
111 {
112 fpidiv(&n, &m, d);
113 }
114
115 /*
116 * arm unary operations
117 */
118
119 static void
fmov(Internal * m,Internal * d)120 fmov(Internal *m, Internal *d)
121 {
122 *d = *m;
123 }
124
125 static void
fmovn(Internal * m,Internal * d)126 fmovn(Internal *m, Internal *d)
127 {
128 *d = *m;
129 d->s ^= 1;
130 }
131
132 static void
fabsf(Internal * m,Internal * d)133 fabsf(Internal *m, Internal *d)
134 {
135 *d = *m;
136 d->s = 0;
137 }
138
139 static void
frnd(Internal * m,Internal * d)140 frnd(Internal *m, Internal *d)
141 {
142 short e;
143
144 (m->s? fsub: fadd)(fpconst[6], *m, d);
145 if(IsWeird(d))
146 return;
147 fpiround(d);
148 e = (d->e - ExpBias) + 1;
149 if(e <= 0)
150 SetZero(d);
151 else if(e > FractBits){
152 if(e < 2*FractBits)
153 d->l &= ~((1<<(2*FractBits - e))-1);
154 }else{
155 d->l = 0;
156 if(e < FractBits)
157 d->h &= ~((1<<(FractBits-e))-1);
158 }
159 }
160
161 static FP1 optab1[16] = { /* Fd := OP Fm */
162 [0] {"MOVF", fmov},
163 [1] {"NEGF", fmovn},
164 [2] {"ABSF", fabsf},
165 [3] {"RNDF", frnd},
166 [4] {"SQTF", /*fsqt*/0},
167 /* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
168 /* URD and NRM aren't implemented */
169 };
170
171 static FP2 optab2[16] = { /* Fd := Fn OP Fm */
172 [0] {"ADDF", fadd},
173 [1] {"MULF", fmul},
174 [2] {"SUBF", fsub},
175 [3] {"RSUBF", fsubr},
176 [4] {"DIVF", fdiv},
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 */
183 /* POL deprecated */
184 };
185
186 static ulong
fcmp(Internal * n,Internal * m)187 fcmp(Internal *n, Internal *m)
188 {
189 int i;
190 Internal rm, rn;
191
192 if(IsWeird(m) || IsWeird(n)){
193 /* BUG: should trap if not masked */
194 return V|C;
195 }
196 rn = *n;
197 rm = *m;
198 fpiround(&rn);
199 fpiround(&rm);
200 i = fpicmp(&rn, &rm);
201 if(i > 0)
202 return C;
203 else if(i == 0)
204 return C|Z;
205 else
206 return N;
207 }
208
209 static void
fld(void (* f)(Internal *,void *),int d,ulong ea,int n,FPsave * ufp)210 fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
211 {
212 void *mem;
213
214 mem = (void*)ea;
215 (*f)(&FR(ufp, d), mem);
216 if(fpemudebug)
217 print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
218 }
219
220 static void
fst(void (* f)(void *,Internal *),ulong ea,int s,int n,FPsave * ufp)221 fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
222 {
223 Internal tmp;
224 void *mem;
225
226 mem = (void*)ea;
227 tmp = FR(ufp, s);
228 if(fpemudebug)
229 print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea);
230 (*f)(mem, &tmp);
231 }
232
233 static int
condok(int cc,int c)234 condok(int cc, int c)
235 {
236 switch(c){
237 case 0: /* Z set */
238 return cc&Z;
239 case 1: /* Z clear */
240 return (cc&Z) == 0;
241 case 2: /* C set */
242 return cc&C;
243 case 3: /* C clear */
244 return (cc&C) == 0;
245 case 4: /* N set */
246 return cc&N;
247 case 5: /* N clear */
248 return (cc&N) == 0;
249 case 6: /* V set */
250 return cc&V;
251 case 7: /* V clear */
252 return (cc&V) == 0;
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 */
266 return 1;
267 case 15: /* never (reserved) */
268 return 0;
269 }
270 return 0; /* not reached */
271 }
272
273 static void
unimp(ulong pc,ulong op)274 unimp(ulong pc, ulong op)
275 {
276 char buf[60];
277
278 snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
279 if(fpemudebug)
280 print("FPE: %s\n", buf);
281 error(buf);
282 /* no return */
283 }
284
285 static void
fpemu(ulong pc,ulong op,Ureg * ur,FPsave * ufp)286 fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
287 {
288 int rn, rd, tag, o;
289 long off;
290 ulong ea;
291 Internal tmp, *fm, *fn;
292
293 /* note: would update fault status here if we noted numeric exceptions */
294
295 /*
296 * LDF, STF; 10.1.1
297 */
298 if(((op>>25)&7) == 6){
299 if(op & (1<<22))
300 unimp(pc, op); /* packed or extended */
301 rn = (op>>16)&0xF;
302 off = (op&0xFF)<<2;
303 if((op & (1<<23)) == 0)
304 off = -off;
305 ea = REG(ur, rn);
306 if(rn == REGPC)
307 ea += 8;
308 if(op & (1<<24))
309 ea += off;
310 rd = (op>>12)&7;
311 if(op & (1<<20)){
312 if(op & (1<<15))
313 fld(fpid2i, rd, ea, 8, ufp);
314 else
315 fld(fpis2i, rd, ea, 4, ufp);
316 }else{
317 if(op & (1<<15))
318 fst(fpii2d, ea, rd, 8, ufp);
319 else
320 fst(fpii2s, ea, rd, 4, ufp);
321 }
322 if((op & (1<<24)) == 0)
323 ea += off;
324 if(op & (1<<21))
325 REG(ur, rn) = ea;
326 return;
327 }
328
329 /*
330 * CPRT/transfer, 10.3
331 */
332 if(op & (1<<4)){
333 rd = (op>>12) & 0xF;
334
335 /*
336 * compare, 10.3.1
337 */
338 if(rd == 15 && op & (1<<20)){
339 rn = (op>>16)&7;
340 fn = &FR(ufp, rn);
341 if(op & (1<<3)){
342 fm = &fpconst[op&7];
343 if(fpemudebug)
344 tag = 'C';
345 }else{
346 fm = &FR(ufp, op&7);
347 if(fpemudebug)
348 tag = 'F';
349 }
350 switch((op>>21)&7){
351 default:
352 unimp(pc, op);
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);
357 break;
358 case 5: /* CNF: Fn :: -Fm */
359 case 7: /* CNFE: Fn :: -Fm (with exception) */
360 tmp = *fm;
361 tmp.s ^= 1;
362 ur->psr &= ~(N|C|Z|V);
363 ur->psr |= fcmp(fn, &tmp);
364 break;
365 }
366 if(fpemudebug)
367 print("CMPF %c%d,F%ld =%#lux\n",
368 tag, rn, op&7, ur->psr>>28);
369 return;
370 }
371
372 /*
373 * other transfer, 10.3
374 */
375 switch((op>>20)&0xF){
376 default:
377 unimp(pc, op);
378 case 0: /* FLT */
379 rn = (op>>16) & 7;
380 fpiw2i(&FR(ufp, rn), ®(ur, rd));
381 if(fpemudebug)
382 print("MOVW[FD] R%d, F%d\n", rd, rn);
383 break;
384 case 1: /* FIX */
385 if(op & (1<<3))
386 unimp(pc, op);
387 rn = op & 7;
388 tmp = FR(ufp, rn);
389 fpii2w(®(ur, rd), &tmp);
390 if(fpemudebug)
391 print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
392 break;
393 case 2: /* FPSR := Rd */
394 ufp->status = REG(ur, rd);
395 if(fpemudebug)
396 print("MOVW R%d, FPSR\n", rd);
397 break;
398 case 3: /* Rd := FPSR */
399 REG(ur, rd) = ufp->status;
400 if(fpemudebug)
401 print("MOVW FPSR, R%d\n", rd);
402 break;
403 case 4: /* FPCR := Rd */
404 ufp->control = REG(ur, rd);
405 if(fpemudebug)
406 print("MOVW R%d, FPCR\n", rd);
407 break;
408 case 5: /* Rd := FPCR */
409 REG(ur, rd) = ufp->control;
410 if(fpemudebug)
411 print("MOVW FPCR, R%d\n", rd);
412 break;
413 }
414 return;
415 }
416
417 /*
418 * arithmetic
419 */
420
421 if(op & (1<<3)){ /* constant */
422 fm = &fpconst[op&7];
423 if(fpemudebug)
424 tag = 'C';
425 }else{
426 fm = &FR(ufp, op&7);
427 if(fpemudebug)
428 tag = 'F';
429 }
430 rd = (op>>12)&7;
431 o = (op>>20)&0xF;
432 if(op & (1<<15)){ /* monadic */
433 FP1 *fp;
434 fp = &optab1[o];
435 if(fp->f == nil)
436 unimp(pc, op);
437 if(fpemudebug)
438 print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd);
439 (*fp->f)(fm, &FR(ufp, rd));
440 } else {
441 FP2 *fp;
442 fp = &optab2[o];
443 if(fp->f == nil)
444 unimp(pc, op);
445 rn = (op>>16)&7;
446 if(fpemudebug)
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));
449 }
450 }
451
452 void
casemu(ulong pc,ulong op,Ureg * ur)453 casemu(ulong pc, ulong op, Ureg *ur)
454 {
455 ulong *rp, ro, rn, *rd;
456
457 USED(pc);
458
459 rp = (ulong*)ur;
460 ro = rp[op>>16 & 0x7];
461 rn = rp[op>>0 & 0x7];
462 rd = rp + (op>>12 & 0x7);
463 rp = (ulong*)*rd;
464 validaddr((ulong)rp, 4, 1);
465 splhi();
466 if(*rd = (*rp == ro))
467 *rp = rn;
468 spllo();
469 }
470
471 void
dmb(ulong pc,ulong op,Ureg * ur)472 dmb(ulong pc, ulong op, Ureg *ur)
473 {
474 USED(pc);
475 USED(op);
476 USED(ur);
477 }
478
479 int ldrexvalid;
480
481 void
ldrex(ulong pc,ulong op,Ureg * ur)482 ldrex(ulong pc, ulong op, Ureg *ur)
483 {
484 ulong *rp, *rd, *addr;
485
486 USED(pc);
487
488 rp = (ulong*)ur;
489 rd = rp + (op>>16 & 0x7);
490 addr = (ulong*)*rd;
491 validaddr((ulong)addr, 4, 0);
492 ldrexvalid = 1;
493 rp[op>>12 & 0x7] = *addr;
494 if(fpemudebug)
495 print("ldrex, r%ld = [r%ld]@0x%8.8p = 0x%8.8lux",
496 op>>12 & 0x7, op>>16 & 0x7, addr, rp[op>>12 & 0x7]);
497 }
498
499 void
clrex(ulong,ulong,Ureg *)500 clrex(ulong, ulong, Ureg *)
501 {
502 ldrexvalid = 0;
503 if(fpemudebug)
504 print("clrex");
505 }
506
507 void
strex(ulong pc,ulong op,Ureg * ur)508 strex(ulong pc, ulong op, Ureg *ur)
509 {
510 ulong *rp, rn, *rd, *addr;
511
512 USED(pc);
513
514 rp = (ulong*)ur;
515 rd = rp + (op>>16 & 0x7);
516 rn = rp[op>>0 & 0x7];
517 addr = (ulong*)*rd;
518 validaddr((ulong)addr, 4, 1);
519 splhi();
520 if(ldrexvalid){
521 if(fpemudebug)
522 print("strex valid, [r%ld]@0x%8.8p = r%ld = 0x%8.8lux",
523 op>>16 & 0x7, addr, op>>0 & 0x7, rn);
524 *addr = rn;
525 ldrexvalid = 0;
526 rp[op>>12 & 0x7] = 0;
527 }else{
528 if(fpemudebug)
529 print("strex invalid, r%ld = 1", op>>16 & 0x7);
530 rp[op>>12 & 0x7] = 1;
531 }
532 spllo();
533 }
534
535 struct {
536 ulong opc;
537 ulong mask;
538 void (*f)(ulong, ulong, Ureg*);
539 } specialopc[] = {
540 { 0x01900f9f, 0x0ff00fff, ldrex },
541 { 0x01800f90, 0x0ff00ff0, strex },
542 { 0xf57ff01f, 0xffffffff, clrex },
543 { 0x0ed00100, 0x0ef08100, casemu },
544 { 0xee070fba, 0xffffffff, dmb },
545 { 0x00000000, 0x00000000, nil }
546 };
547
548 /*
549 * returns the number of FP instructions emulated
550 */
551 int
fpiarm(Ureg * ur)552 fpiarm(Ureg *ur)
553 {
554 ulong op, o;
555 FPsave *ufp;
556 int i, n;
557
558 if(up == nil)
559 panic("fpiarm not in a process");
560 ufp = &up->fpsave;
561 /* because all the state is in the proc structure,
562 * it need not be saved/restored
563 */
564 if(up->fpstate != FPactive){
565 // assert(sizeof(Internal) == sizeof(ufp->regs[0]));
566 up->fpstate = FPactive;
567 ufp->control = 0;
568 ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */
569 for(n = 0; n < 8; n++)
570 FR(ufp, n) = fpconst[0];
571 }
572 for(n=0; ;n++){
573 validaddr(ur->pc, 4, 0);
574 op = *(ulong*)(ur->pc);
575 if(fpemudebug)
576 print("%#lux: %#8.8lux ", ur->pc, op);
577 o = (op>>24) & 0xF;
578 if(condok(ur->psr, op>>28)){
579 for(i = 0; specialopc[i].f; i++)
580 if((op & specialopc[i].mask) == specialopc[i].opc)
581 break;
582 if(specialopc[i].f)
583 specialopc[i].f(ur->pc, op, ur);
584 else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
585 break;
586 else
587 fpemu(ur->pc, op, ur, ufp);
588 }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
589 break;
590 ur->pc += 4;
591 }
592 if(fpemudebug) print("\n");
593 return n;
594 }
595