fpiarm.c 11 KB

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