]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/kw/fpiarm.c
Import sources from 2011-03-30 iso image
[plan9front.git] / sys / src / 9 / kw / 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
13 #include        "ureg.h"
14
15 #include        "arm.h"
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 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
78 fadd(Internal m, Internal n, Internal *d)
79 {
80         (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
81 }
82
83 static void
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
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
98 fmul(Internal m, Internal n, Internal *d)
99 {
100         fpimul(&m, &n, d);
101 }
102
103 static void
104 fdiv(Internal m, Internal n, Internal *d)
105 {
106         fpidiv(&m, &n, d);
107 }
108
109 static void
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
120 fmov(Internal *m, Internal *d)
121 {
122         *d = *m;
123 }
124
125 static void
126 fmovn(Internal *m, Internal *d)
127 {
128         *d = *m;
129         d->s ^= 1;
130 }
131
132 static void
133 fabsf(Internal *m, Internal *d)
134 {
135         *d = *m;
136         d->s = 0;
137 }
138
139 static void
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
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
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
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
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
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
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), &REG(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(&REG(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
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 int ldrexvalid;
472
473 void
474 ldrex(ulong pc, ulong op, Ureg *ur)
475 {
476         ulong *rp, *rd, *addr;
477
478         USED(pc);
479
480         rp = (ulong*)ur;
481         rd = rp + (op>>16 & 0x7);
482         addr = (ulong*)*rd;
483         validaddr((ulong)addr, 4, 0);
484         ldrexvalid = 1;
485         rp[op>>12 & 0x7] = *addr;
486         if(fpemudebug)
487                 print("ldrex, r%ld = [r%ld]@0x%8.8p = 0x%8.8lux",
488                         op>>12 & 0x7, op>>16 & 0x7, addr, rp[op>>12 & 0x7]);
489 }
490
491 void
492 strex(ulong pc, ulong op, Ureg *ur)
493 {
494         ulong *rp, rn, *rd, *addr;
495
496         USED(pc);
497
498         rp = (ulong*)ur;
499         rd = rp + (op>>16 & 0x7);
500         rn = rp[op>>0 & 0x7];
501         addr = (ulong*)*rd;
502         validaddr((ulong)addr, 4, 1);
503         splhi();
504         if(ldrexvalid){
505                 if(fpemudebug)
506                         print("strex valid, [r%ld]@0x%8.8p = r%ld = 0x%8.8lux",
507                                 op>>16 & 0x7, addr, op>>0 & 0x7, rn);
508                 *addr = rn;
509                 ldrexvalid = 0;
510                 rp[op>>12 & 0x7] = 0;
511         }else{
512                 if(fpemudebug)
513                         print("strex invalid, r%ld = 1", op>>16 & 0x7);
514                 rp[op>>12 & 0x7] = 1;
515         }
516         spllo();
517 }
518
519 struct {
520         ulong   opc;
521         ulong   mask;
522         void    (*f)(ulong, ulong, Ureg*);
523 } specialopc[] = {
524         { 0x01900f9f, 0x0ff00fff, ldrex },
525         { 0x01800f90, 0x0ff00ff0, strex },
526         { 0x0ed00100, 0x0ef08100, casemu },
527         { 0x00000000, 0x00000000, nil }
528 };
529
530 /*
531  * returns the number of FP instructions emulated
532  */
533 int
534 fpiarm(Ureg *ur)
535 {
536         ulong op, o;
537         FPsave *ufp;
538         int i, n;
539
540         if(up == nil)
541                 panic("fpiarm not in a process");
542         ufp = &up->fpsave;
543         /* because all the state is in the proc structure,
544          * it need not be saved/restored
545          */
546         if(up->fpstate != FPactive){
547 //              assert(sizeof(Internal) == sizeof(ufp->regs[0]));
548                 up->fpstate = FPactive;
549                 ufp->control = 0;
550                 ufp->status = (0x01<<28)|(1<<12);       /* software emulation, alternative C flag */
551                 for(n = 0; n < 8; n++)
552                         FR(ufp, n) = fpconst[0];
553         }
554         for(n=0; ;n++){
555                 validaddr(ur->pc, 4, 0);
556                 op = *(ulong*)(ur->pc);
557                 if(fpemudebug)
558                         print("%#lux: %#8.8lux ", ur->pc, op);
559                 o = (op>>24) & 0xF;
560                 if(condok(ur->psr, op>>28)){
561                         for(i = 0; specialopc[i].f; i++)
562                                 if((op & specialopc[i].mask) == specialopc[i].opc)
563                                         break;
564                         if(specialopc[i].f)
565                                 specialopc[i].f(ur->pc, op, ur);
566                         else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
567                                 break;
568                         else
569                                 fpemu(ur->pc, op, ur, ufp);
570                 }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
571                         break;
572                 ur->pc += 4;
573         }
574         if(fpemudebug) print("\n");
575         return n;
576 }