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