fpiarm.c 11 KB

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