fpiarm.c 9.6 KB

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