]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/fpiarm.c
add raspberry pi kernel (from sources)
[plan9front.git] / sys / src / 9 / bcm / 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 #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
83 fadd(Internal m, Internal n, Internal *d)
84 {
85         (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
86 }
87
88 static void
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
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
103 fmul(Internal m, Internal n, Internal *d)
104 {
105         fpimul(&m, &n, d);
106 }
107
108 static void
109 fdiv(Internal m, Internal n, Internal *d)
110 {
111         fpidiv(&m, &n, d);
112 }
113
114 static void
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
125 fmov(Internal *m, Internal *d)
126 {
127         *d = *m;
128 }
129
130 static void
131 fmovn(Internal *m, Internal *d)
132 {
133         *d = *m;
134         d->s ^= 1;
135 }
136
137 static void
138 fabsf(Internal *m, Internal *d)
139 {
140         *d = *m;
141         d->s = 0;
142 }
143
144 static void
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
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
219 fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
220 {
221         void *mem;
222
223         mem = (void*)ea;
224         (*f)(&FR(ufp, d), mem);
225         if(fpemudebug)
226                 print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
227 }
228
229 static void
230 fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
231 {
232         Internal tmp;
233         void *mem;
234
235         mem = (void*)ea;
236         tmp = FR(ufp, s);
237         if(fpemudebug)
238                 print("MOV%c    F%d,#%lux\n", n==8? 'D': 'F', s, ea);
239         (*f)(mem, &tmp);
240 }
241
242 static int
243 condok(int cc, int c)
244 {
245         switch(c){
246         case 0: /* Z set */
247                 return cc&Z;
248         case 1: /* Z clear */
249                 return (cc&Z) == 0;
250         case 2: /* C set */
251                 return cc&C;
252         case 3: /* C clear */
253                 return (cc&C) == 0;
254         case 4: /* N set */
255                 return cc&N;
256         case 5: /* N clear */
257                 return (cc&N) == 0;
258         case 6: /* V set */
259                 return cc&V;
260         case 7: /* V clear */
261                 return (cc&V) == 0;
262         case 8: /* C set and Z clear */
263                 return cc&C && (cc&Z) == 0;
264         case 9: /* C clear or Z set */
265                 return (cc&C) == 0 || cc&Z;
266         case 10:        /* N set and V set, or N clear and V clear */
267                 return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
268         case 11:        /* N set and V clear, or N clear and V set */
269                 return (cc&(N|V))==N || (cc&(N|V))==V;
270         case 12:        /* Z clear, and either N set and V set or N clear and V clear */
271                 return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
272         case 13:        /* Z set, or N set and V clear or N clear and V set */
273                 return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
274         case 14:        /* always */
275                 return 1;
276         case 15:        /* never (reserved) */
277                 return 0;
278         }
279         return 0;       /* not reached */
280 }
281
282 static void
283 unimp(ulong pc, ulong op)
284 {
285         char buf[60];
286
287         snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
288         if(fpemudebug)
289                 print("FPE: %s\n", buf);
290         error(buf);
291         /* no return */
292 }
293
294 static void
295 fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
296 {
297         int rn, rd, tag, o;
298         long off;
299         ulong ea;
300         Internal tmp, *fm, *fn;
301
302         /* note: would update fault status here if we noted numeric exceptions */
303
304         /*
305          * LDF, STF; 10.1.1
306          */
307         if(((op>>25)&7) == 6){
308                 if(op & (1<<22))
309                         unimp(pc, op);  /* packed or extended */
310                 rn = (op>>16)&0xF;
311                 off = (op&0xFF)<<2;
312                 if((op & (1<<23)) == 0)
313                         off = -off;
314                 ea = REG(ur, rn);
315                 if(rn == REGPC)
316                         ea += 8;
317                 if(op & (1<<24))
318                         ea += off;
319                 rd = (op>>12)&7;
320                 if(op & (1<<20)){
321                         if(op & (1<<15))
322                                 fld(fpid2i, rd, ea, 8, ufp);
323                         else
324                                 fld(fpis2i, rd, ea, 4, ufp);
325                 }else{
326                         if(op & (1<<15))
327                                 fst(fpii2d, ea, rd, 8, ufp);
328                         else
329                                 fst(fpii2s, ea, rd, 4, ufp);
330                 }
331                 if((op & (1<<24)) == 0)
332                         ea += off;
333                 if(op & (1<<21))
334                         REG(ur, rn) = ea;
335                 return;
336         }
337
338         /*
339          * CPRT/transfer, 10.3
340          */
341         if(op & (1<<4)){
342                 rd = (op>>12) & 0xF;
343
344                 /*
345                  * compare, 10.3.1
346                  */
347                 if(rd == 15 && op & (1<<20)){
348                         rn = (op>>16)&7;
349                         fn = &FR(ufp, rn);
350                         if(op & (1<<3)){
351                                 fm = &fpconst[op&7];
352                                 if(fpemudebug)
353                                         tag = 'C';
354                         }else{
355                                 fm = &FR(ufp, op&7);
356                                 if(fpemudebug)
357                                         tag = 'F';
358                         }
359                         switch((op>>21)&7){
360                         default:
361                                 unimp(pc, op);
362                         case 4: /* CMF: Fn :: Fm */
363                         case 6: /* CMFE: Fn :: Fm (with exception) */
364                                 ur->psr &= ~(N|C|Z|V);
365                                 ur->psr |= fcmp(fn, fm);
366                                 break;
367                         case 5: /* CNF: Fn :: -Fm */
368                         case 7: /* CNFE: Fn :: -Fm (with exception) */
369                                 tmp = *fm;
370                                 tmp.s ^= 1;
371                                 ur->psr &= ~(N|C|Z|V);
372                                 ur->psr |= fcmp(fn, &tmp);
373                                 break;
374                         }
375                         if(fpemudebug)
376                                 print("CMPF     %c%d,F%ld =%#lux\n",
377                                         tag, rn, op&7, ur->psr>>28);
378                         return;
379                 }
380
381                 /*
382                  * other transfer, 10.3
383                  */
384                 switch((op>>20)&0xF){
385                 default:
386                         unimp(pc, op);
387                 case 0: /* FLT */
388                         rn = (op>>16) & 7;
389                         fpiw2i(&FR(ufp, rn), &REG(ur, rd));
390                         if(fpemudebug)
391                                 print("MOVW[FD] R%d, F%d\n", rd, rn);
392                         break;
393                 case 1: /* FIX */
394                         if(op & (1<<3))
395                                 unimp(pc, op);
396                         rn = op & 7;
397                         tmp = FR(ufp, rn);
398                         fpii2w(&REG(ur, rd), &tmp);
399                         if(fpemudebug)
400                                 print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
401                         break;
402                 case 2: /* FPSR := Rd */
403                         ufp->status = REG(ur, rd);
404                         if(fpemudebug)
405                                 print("MOVW     R%d, FPSR\n", rd);
406                         break;
407                 case 3: /* Rd := FPSR */
408                         REG(ur, rd) = ufp->status;
409                         if(fpemudebug)
410                                 print("MOVW     FPSR, R%d\n", rd);
411                         break;
412                 case 4: /* FPCR := Rd */
413                         ufp->control = REG(ur, rd);
414                         if(fpemudebug)
415                                 print("MOVW     R%d, FPCR\n", rd);
416                         break;
417                 case 5: /* Rd := FPCR */
418                         REG(ur, rd) = ufp->control;
419                         if(fpemudebug)
420                                 print("MOVW     FPCR, R%d\n", rd);
421                         break;
422                 }
423                 return;
424         }
425
426         /*
427          * arithmetic
428          */
429
430         if(op & (1<<3)){        /* constant */
431                 fm = &fpconst[op&7];
432                 if(fpemudebug)
433                         tag = 'C';
434         }else{
435                 fm = &FR(ufp, op&7);
436                 if(fpemudebug)
437                         tag = 'F';
438         }
439         rd = (op>>12)&7;
440         o = (op>>20)&0xF;
441         if(op & (1<<15)){       /* monadic */
442                 FP1 *fp;
443                 fp = &optab1[o];
444                 if(fp->f == nil)
445                         unimp(pc, op);
446                 if(fpemudebug)
447                         print("%s       %c%ld,F%d\n", fp->name, tag, op&7, rd);
448                 (*fp->f)(fm, &FR(ufp, rd));
449         } else {
450                 FP2 *fp;
451                 fp = &optab2[o];
452                 if(fp->f == nil)
453                         unimp(pc, op);
454                 rn = (op>>16)&7;
455                 if(fpemudebug)
456                         print("%s       %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
457                 (*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
458         }
459 }
460
461 /*
462  * returns the number of FP instructions emulated
463  */
464 int
465 fpiarm(Ureg *ur)
466 {
467         ulong op, o, cp;
468         FPsave *ufp;
469         int n;
470
471         if(up == nil)
472                 panic("fpiarm not in a process");
473         ufp = &up->fpsave;
474         /*
475          * because all the emulated fp state is in the proc structure,
476          * it need not be saved/restored
477          */
478         switch(up->fpstate){
479         case FPactive:
480         case FPinactive:
481                 error("illegal instruction: emulated fpu opcode in VFP mode");
482         case FPinit:
483                 assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
484                 up->fpstate = FPemu;
485                 ufp->control = 0;
486                 ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
487                 for(n = 0; n < 8; n++)
488                         FR(ufp, n) = fpconst[0];
489         }
490         for(n=0; ;n++){
491                 validaddr(ur->pc, 4, 0);
492                 op = *(ulong*)(ur->pc);
493                 if(fpemudebug)
494                         print("%#lux: %#8.8lux ", ur->pc, op);
495                 o = (op>>24) & 0xF;
496                 cp = (op>>8) & 0xF;
497                 if(!ISFPAOP(cp, o))
498                         break;
499                 if(condok(ur->psr, op>>28))
500                         fpemu(ur->pc, op, ur, ufp);
501                 ur->pc += 4;            /* pretend cpu executed the instr */
502         }
503         if(fpemudebug)
504                 print("\n");
505         return n;
506 }