int64p.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team
  4. This file contains some helper routines for int64 and qword
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$define FPC_SYSTEM_HAS_SHR_QWORD}
  12. // Simplistic version with checking if whole bytes can be shifted
  13. // Doesn't change bitshift portion even if possible because of byteshift
  14. // Shorter code but not shortest execution time version
  15. function fpc_shr_qword(value: qword; shift: ALUUInt): qword; assembler; nostackframe;
  16. [public, alias: 'FPC_SHR_QWORD']; compilerproc;
  17. label
  18. byteshift, bitshift, finish;
  19. asm
  20. // value passed in R25...R18
  21. // shift passed in R16
  22. // return value in R25...R18
  23. push R16
  24. andi R16, 63 // mask 64 bit relevant value per generic routine
  25. byteshift:
  26. breq finish // shift = 0, finished
  27. cpi R16, 8 // Check if shift is at least a byte
  28. brlo bitshift
  29. mov R18, R19 // if so, then shift all bytes right by 1 position
  30. mov R19, R20
  31. mov R20, R21
  32. mov R21, R22
  33. mov R22, R23
  34. mov R23, R24
  35. mov R24, R25
  36. clr R25 // and clear the high byte
  37. subi R16, 8 // subtract 8 bits from shift
  38. rjmp byteshift // check if another byte can be shifted
  39. bitshift: // shift all 8 bytes right by 1 bit
  40. lsr R25
  41. ror R24
  42. ror R23
  43. ror R22
  44. ror R21
  45. ror R20
  46. ror R19
  47. ror R18
  48. dec R16
  49. brne bitshift // until R16 = 0
  50. finish:
  51. pop R16
  52. end;
  53. function fpc_shr_qword(value: qword; shift: ALUUInt): qword; external name 'FPC_SHR_QWORD';
  54. {$define FPC_SYSTEM_HAS_SHL_QWORD}
  55. function fpc_shl_qword(value: qword; shift: ALUUInt): qword; assembler; nostackframe;
  56. [public, alias: 'FPC_SHL_QWORD']; compilerproc;
  57. label
  58. byteshift, bitshift, finish;
  59. asm
  60. // value passed in R25...R18
  61. // shift passed in R16
  62. // return value in R25...R18
  63. push R16
  64. andi R16, 63 // mask 64 bit relevant value per generic routine
  65. byteshift:
  66. breq finish // shift = 0, finished
  67. cpi R16, 8 // Check if shift is at least a byte
  68. brlo bitshift
  69. mov R25, R24 // if so, then shift all bytes left by 1 position
  70. mov R24, R23
  71. mov R23, R22
  72. mov R22, R21
  73. mov R21, R20
  74. mov R20, R19
  75. mov R19, R18
  76. clr R18 // and clear the high byte
  77. subi R16, 8 // subtract 8 bits from shift
  78. rjmp byteshift // check if another byte can be shifted
  79. bitshift: // shift all 8 bytes left by 1 bit
  80. lsl R18
  81. rol R19
  82. rol R20
  83. rol R21
  84. rol R22
  85. rol R23
  86. rol R24
  87. rol R25
  88. dec R16
  89. brne bitshift // until R16 = 0
  90. finish:
  91. pop R16
  92. end;
  93. function fpc_shl_qword(value: qword; shift: ALUUInt): qword; external name 'FPC_SHL_QWORD';
  94. {$define FPC_SYSTEM_HAS_SHL_INT64}
  95. function fpc_shl_int64(value: int64; shift: ALUUInt): int64;
  96. [public, alias: 'FPC_SHL_INT64']; compilerproc; inline;
  97. begin
  98. Result := fpc_shl_qword(qword(value), shift);
  99. end;
  100. {$define FPC_SYSTEM_HAS_SHR_INT64}
  101. // shr of signed int is same as shr of unsigned int (logical shift right)
  102. function fpc_shr_int64(value: int64; shift: ALUUInt): int64; [public, alias: 'FPC_SHR_INT64']; compilerproc;
  103. begin
  104. Result := fpc_shr_qword(qword(value), shift);
  105. end;
  106. {$define FPC_SYSTEM_HAS_DIV_QWORD}
  107. function fpc_div_qword(n,z : qword): qword; nostackframe; assembler; [public,alias: 'FPC_DIV_QWORD']; compilerproc;
  108. label
  109. start, div1, div2, div3, finish;
  110. asm
  111. // Symbol Name Register(s)
  112. // z (A) dividend R17, R16, R15, R14, R13, R12, R11, R10
  113. // n (B) divisor R25, R24, R23, R22, R21, R20, R19, R18
  114. // r (P) remainder R9, R8, R7, R6, R5, R4, R3, R2
  115. // i counter R26
  116. // 1 R27
  117. cp R25, R1
  118. cpc R24, R1
  119. cpc R23, R1
  120. cpc R22, R1
  121. cpc R21, R1
  122. cpc R20, R1
  123. cpc R19, R1
  124. cpc R18, R1
  125. brne .LNonZero
  126. {$ifdef CPUAVR_HAS_JMP_CALL}
  127. call fpc_divbyzero
  128. {$else CPUAVR_HAS_JMP_CALL}
  129. rcall fpc_divbyzero
  130. {$endif CPUAVR_HAS_JMP_CALL}
  131. .LNonZero:
  132. push R17
  133. push R16
  134. push R15
  135. push R14
  136. push R13
  137. push R12
  138. push R11
  139. push R10
  140. push R9
  141. push R8
  142. push R7
  143. push R6
  144. push R5
  145. push R4
  146. push R3
  147. push R2
  148. ldi R27, 1 // needed below for OR instruction
  149. start: // Start of division...
  150. clr R9 // clear remainder
  151. clr R8
  152. clr R7
  153. clr R6
  154. clr R5
  155. clr R4
  156. clr R3
  157. clr R2
  158. ldi R26, 64 // iterate over 64 bits
  159. div1:
  160. lsl R10 // shift left A_L
  161. rol R11
  162. rol R12
  163. rol R13
  164. rol R14
  165. rol R15
  166. rol R16
  167. rol R17
  168. rol R2 // shift left P with carry from A shift
  169. rol R3
  170. rol R4
  171. rol R5
  172. rol R6
  173. rol R7
  174. rol R8
  175. rol R9
  176. sub R2, R18 // Subtract B from P, P <= P - B
  177. sbc R3, R19
  178. sbc R4, R20
  179. sbc R5, R21
  180. sbc R6, R22
  181. sbc R7, R23
  182. sbc R8, R24
  183. sbc R9, R25
  184. brlo div2
  185. or R10, R27 // Set A[0] = 1
  186. rjmp div3
  187. div2: // negative branch, A[0] = 0 (default after shift), restore P
  188. add R2, R18 // restore old value of P
  189. adc R3, R19
  190. adc R4, R20
  191. adc R5, R21
  192. adc R6, R22
  193. adc R7, R23
  194. adc R8, R24
  195. adc R9, R25
  196. div3:
  197. dec R26
  198. breq finish
  199. rjmp div1
  200. finish:
  201. mov R25, R17 // Move answer from R17..10 to R25..18
  202. mov R24, R16
  203. mov R23, R15
  204. mov R22, R14
  205. mov R21, R13
  206. mov R20, R12
  207. mov R19, R11
  208. mov R18, R10
  209. pop R2
  210. pop R3
  211. pop R4
  212. pop R5
  213. pop R6
  214. pop R7
  215. pop R8
  216. pop R9
  217. pop R10
  218. pop R11
  219. pop R12
  220. pop R13
  221. pop R14
  222. pop R15
  223. pop R16
  224. pop R17
  225. end;
  226. function fpc_div_qword(n,z : qword): qword; external name 'FPC_DIV_QWORD';
  227. {$define FPC_SYSTEM_HAS_MOD_QWORD}
  228. function fpc_mod_qword(n,z : qword): qword; nostackframe; assembler; [public,alias: 'FPC_MOD_QWORD']; compilerproc;
  229. label
  230. start, div1, div2, div3, finish;
  231. asm
  232. // Symbol Name Register(s)
  233. // z (A) dividend R17, R16, R15, R14, R13, R12, R11, R10
  234. // n (B) divisor R25, R24, R23, R22, R21, R20, R19, R18
  235. // r (P) remainder R9, R8, R7, R6, R5, R4, R3, R2
  236. // i counter R26
  237. // 1 R27
  238. cp R25, R1
  239. cpc R24, R1
  240. cpc R23, R1
  241. cpc R22, R1
  242. cpc R21, R1
  243. cpc R20, R1
  244. cpc R19, R1
  245. cpc R18, R1
  246. brne .LNonZero
  247. {$ifdef CPUAVR_HAS_JMP_CALL}
  248. call fpc_divbyzero
  249. {$else CPUAVR_HAS_JMP_CALL}
  250. rcall fpc_divbyzero
  251. {$endif CPUAVR_HAS_JMP_CALL}
  252. .LNonZero:
  253. push R17
  254. push R16
  255. push R15
  256. push R14
  257. push R13
  258. push R12
  259. push R11
  260. push R10
  261. push R9
  262. push R8
  263. push R7
  264. push R6
  265. push R5
  266. push R4
  267. push R3
  268. push R2
  269. ldi R27, 1
  270. start: // Start of division...
  271. clr R9 // clear remainder
  272. clr R8
  273. clr R7
  274. clr R6
  275. clr R5
  276. clr R4
  277. clr R3
  278. clr R2
  279. ldi R26, 64 // iterate over 64 bits
  280. div1:
  281. lsl R10 // shift left A_L
  282. rol R11
  283. rol R12
  284. rol R13
  285. rol R14
  286. rol R15
  287. rol R16
  288. rol R17
  289. rol R2 // shift left P with carry from A shift
  290. rol R3
  291. rol R4
  292. rol R5
  293. rol R6
  294. rol R7
  295. rol R8
  296. rol R9
  297. sub R2, R18 // Subtract B from P, P <= P - B
  298. sbc R3, R19
  299. sbc R4, R20
  300. sbc R5, R21
  301. sbc R6, R22
  302. sbc R7, R23
  303. sbc R8, R24
  304. sbc R9, R25
  305. brlo div2
  306. or R10, R27 // Set A[0] = 1
  307. rjmp div3
  308. div2: // negative branch, A[0] = 0 (default after shift), restore P
  309. add R2, R18 // restore old value of P
  310. adc R3, R19
  311. adc R4, R20
  312. adc R5, R21
  313. adc R6, R22
  314. adc R7, R23
  315. adc R8, R24
  316. adc R9, R25
  317. div3:
  318. dec R26
  319. breq finish
  320. rjmp div1
  321. finish:
  322. mov R25, R9 // Move answer from R9..2 to R25..18
  323. mov R24, R8
  324. mov R23, R7
  325. mov R22, R6
  326. mov R21, R5
  327. mov R20, R4
  328. mov R19, R3
  329. mov R18, R2
  330. pop R2
  331. pop R3
  332. pop R4
  333. pop R5
  334. pop R6
  335. pop R7
  336. pop R8
  337. pop R9
  338. pop R10
  339. pop R11
  340. pop R12
  341. pop R13
  342. pop R14
  343. pop R15
  344. pop R16
  345. pop R17
  346. end;
  347. function fpc_mod_qword(n,z : qword): qword; external name 'FPC_MOD_QWORD';
  348. {$define FPC_SYSTEM_HAS_DIV_INT64}
  349. function fpc_div_int64(n,z : int64) : int64; nostackframe; assembler; [public,alias: 'FPC_DIV_INT64']; compilerproc;
  350. label
  351. pos1, pos2, fin;
  352. asm
  353. // Convert n, z to unsigned int, then call div_qword,
  354. // Restore sign if high bits of n xor z is negative
  355. // n divisor R25, R24, R23, R22, R21, R20, R19, R18
  356. // z dividend R17, R16, R15, R14, R13, R12, R11, R10
  357. // neg_result R30
  358. // one R31
  359. mov R30, R17 // store hi8(z)
  360. eor R30, R25 // hi8(z) XOR hi8(n), answer must be negative if MSB set
  361. // convert n to absolute
  362. ldi R31, 1 // 1 in R31 used later
  363. sub R25, r1 // subtract 0, just to check sign flag
  364. brpl pos1
  365. com R25
  366. com R24
  367. com R23
  368. com R22
  369. com R21
  370. com R20
  371. com R19
  372. com R18
  373. add R18, R31 // add 1
  374. adc R19, R1 // add carry bit
  375. adc R20, R1
  376. adc R21, R1
  377. adc R22, R1
  378. adc R23, R1
  379. adc R24, R1
  380. adc R25, R1
  381. pos1:
  382. sub R17, R1
  383. brpl pos2
  384. com R17
  385. com R16
  386. com R15
  387. com R14
  388. com R13
  389. com R12
  390. com R11
  391. com R10
  392. add R10, R31
  393. adc R11, R1
  394. adc R12, R1
  395. adc R13, R1
  396. adc R14, R1
  397. adc R15, R1
  398. adc R16, R1
  399. adc R17, R1
  400. pos2:
  401. {$ifdef CPUAVR_HAS_JMP_CALL}
  402. call fpc_div_qword
  403. {$else CPUAVR_HAS_JMP_CALL}
  404. rcall fpc_div_qword
  405. {$endif CPUAVR_HAS_JMP_CALL}
  406. sbrs R30, 7 // skip if bit 7 is cleared (result should be positive)
  407. rjmp fin
  408. com R25 // result from FPC_DIV_WORD in R25 ... R22
  409. com R24
  410. com R23
  411. com R22
  412. com R21
  413. com R20
  414. com R19
  415. com R18
  416. ldi R31, 1
  417. add R18, R31 // add 1
  418. adc R19, R1 // add carry bit
  419. adc R20, R1
  420. adc R21, R1
  421. adc R22, R1
  422. adc R23, R1
  423. adc R24, R1
  424. adc R25, R1
  425. fin:
  426. end;
  427. {$define FPC_SYSTEM_HAS_MOD_INT64}
  428. function fpc_mod_int64(n,z : int64) : int64; nostackframe; assembler; [public,alias: 'FPC_MOD_INT64']; compilerproc;
  429. label
  430. pos1, pos2, fin;
  431. asm
  432. // Convert n, z to unsigned int, then call mod_qword,
  433. // Restore sign if high bits of n xor z is negative
  434. // n divisor R25, R24, R23, R22, R21, R20, R19, R18
  435. // z dividend R17, R16, R15, R14, R13, R12, R11, R10
  436. // neg_result R30
  437. // one R31
  438. mov R30, R17 // store hi8(z)
  439. // convert n to absolute
  440. ldi R31, 1
  441. sub R25, r1 // subtract 0, just to check sign flag
  442. brpl pos1
  443. com R25
  444. com R24
  445. com R23
  446. com R22
  447. com R21
  448. com R20
  449. com R19
  450. com R18
  451. add R18, R31 // add 1
  452. adc R19, R1 // add carry bit
  453. adc R20, R1
  454. adc R21, R1
  455. adc R22, R1
  456. adc R23, R1
  457. adc R24, R1
  458. adc R25, R1
  459. pos1:
  460. sub R17, R1
  461. brpl pos2
  462. com R17
  463. com R16
  464. com R15
  465. com R14
  466. com R13
  467. com R12
  468. com R11
  469. com R10
  470. add R10, R31
  471. adc R11, R1
  472. adc R12, R1
  473. adc R13, R1
  474. adc R14, R1
  475. adc R15, R1
  476. adc R16, R1
  477. adc R17, R1
  478. pos2:
  479. {$ifdef CPUAVR_HAS_JMP_CALL}
  480. call fpc_mod_qword
  481. {$else CPUAVR_HAS_JMP_CALL}
  482. rcall fpc_mod_qword
  483. {$endif CPUAVR_HAS_JMP_CALL}
  484. sbrs R30, 7 // Not finished if sign bit is set
  485. rjmp fin
  486. com R25 // Convert to 2's complement
  487. com R24 // Complement all bits...
  488. com R23
  489. com R22
  490. com R21
  491. com R20
  492. com R19
  493. com R18
  494. ldi R31, 1
  495. add R18, R31 // ...and add 1 to answer
  496. adc R19, R1
  497. adc R20, R1
  498. adc R21, R1
  499. adc R22, R1
  500. adc R23, R1
  501. adc R24, R1
  502. adc R25, R1
  503. fin:
  504. end;