math.inc 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team.
  4. Implementation of mathematical Routines (only for real)
  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. // Based on restoring division algorithm
  12. // Algorithm source document: Lecture notes by S. Galal and D. Pham, Division algorithms and hardware implementations.
  13. // Link to documentation http://www.seas.ucla.edu/~ingrid/ee213a/lectures/division_presentV2.pdf
  14. // Also refer to description on Wikipedia: https://en.wikipedia.org/wiki/Division_algorithm#Restoring_division
  15. // Note that the algorithm automatically yields the following results for special cases:
  16. // z div 0 = MAX(type)
  17. // 0 div 0 = MAX(type)
  18. // 0 div n = 0
  19. // Checks for z = 0; n = [0,1]; n = z and n > z could shortcut the algorithm for speed-ups
  20. // but would add extra code
  21. // Perhaps add the checks depending on optimization settings?
  22. // z (dividend) = q(quotient) x n(divisor) + p(remainder)
  23. {$ifndef FPC_SYSTEM_HAS_DIV_BYTE}
  24. {$define FPC_SYSTEM_HAS_DIV_BYTE}
  25. // z in Ra, n in Rb, 0 in Rp
  26. function fpc_div_byte(n, z: byte): byte; assembler; nostackframe;
  27. {$ifdef FPC_IS_SYSTEM}[public,alias: 'FPC_DIV_BYTE'];{$endif}
  28. label
  29. start, div1, div2, div3, finish;
  30. asm
  31. // Symbol Name Register(s)
  32. // z (A) dividend R22
  33. // n (B) divisor R24
  34. // p (P) remainder R20
  35. // i counter R18
  36. cp R24, R1
  37. brne start
  38. {$ifdef CPUAVR_HAS_JMP_CALL}
  39. call get_pc_addr
  40. {$else CPUAVR_HAS_JMP_CALL}
  41. rcall get_pc_addr
  42. {$endif CPUAVR_HAS_JMP_CALL}
  43. movw R20, R24
  44. {$ifdef CPUAVR_HAS_JMP_CALL}
  45. call get_frame
  46. {$else CPUAVR_HAS_JMP_CALL}
  47. rcall get_frame
  48. {$endif CPUAVR_HAS_JMP_CALL}
  49. movw R18, R24
  50. ldi R22, 200
  51. clr R23
  52. clr R24
  53. clr R25
  54. {$ifdef CPUAVR_HAS_JMP_CALL}
  55. call HandleErrorAddrFrameInd
  56. {$else CPUAVR_HAS_JMP_CALL}
  57. rcall HandleErrorAddrFrameInd
  58. {$endif CPUAVR_HAS_JMP_CALL}
  59. start:
  60. clr R20 // clear remainder
  61. ldi R18, 8 // iterate over 8 bits
  62. div1:
  63. lsl R22 // shift left A
  64. rol R20 // shift left P with carry from A shift
  65. sub R20, R24 // Subtract B from P, P <= P - B
  66. brlo div2
  67. ori R22, 1 // Set A[0] = 1
  68. rjmp div3
  69. div2: // negative branch, A[0] = 0 (default after shift), restore P
  70. add R20, R24 // restore old value of P
  71. div3:
  72. dec R18
  73. brne div1
  74. finish:
  75. mov R24, R22 // Move result from R22 to R24
  76. end;
  77. {It is a compilerproc (systemh.inc), make an alias for internal use.}
  78. {$ifdef FPC_IS_SYSTEM}
  79. function fpc_div_byte(n, z: byte): byte; external name 'FPC_DIV_BYTE';
  80. {$endif FPC_IS_SYSTEM}
  81. {$endif FPC_SYSTEM_HAS_DIV_BYTE}
  82. {$ifndef FPC_SYSTEM_HAS_DIV_WORD}
  83. {$define FPC_SYSTEM_HAS_DIV_WORD}
  84. // z in Ra, n in Rb, 0 in Rp
  85. function fpc_div_word(n, z: word): word; assembler; nostackframe;
  86. {$ifdef FPC_IS_SYSTEM}[public,alias: 'FPC_DIV_WORD'];{$endif}
  87. label
  88. start, div1, div2, div3, finish;
  89. asm
  90. // Symbol Name Register(s)
  91. // z (A) dividend R23, R22
  92. // n (B) divisor R25, R24
  93. // p (P) remainder R21, R20
  94. // i counter R18
  95. cp R24, R1
  96. brne start
  97. {$ifdef CPUAVR_HAS_JMP_CALL}
  98. call get_pc_addr
  99. {$else CPUAVR_HAS_JMP_CALL}
  100. rcall get_pc_addr
  101. {$endif CPUAVR_HAS_JMP_CALL}
  102. movw R20, R24
  103. {$ifdef CPUAVR_HAS_JMP_CALL}
  104. call get_frame
  105. {$else CPUAVR_HAS_JMP_CALL}
  106. rcall get_frame
  107. {$endif CPUAVR_HAS_JMP_CALL}
  108. movw R18, R24
  109. ldi R22, 200
  110. clr R23
  111. clr R24
  112. clr R25
  113. {$ifdef CPUAVR_HAS_JMP_CALL}
  114. call HandleErrorAddrFrameInd
  115. {$else CPUAVR_HAS_JMP_CALL}
  116. rcall HandleErrorAddrFrameInd
  117. {$endif CPUAVR_HAS_JMP_CALL}
  118. start: // Start of division...
  119. clr R20 // clear remainder low
  120. clr R21 // clear remainder hi
  121. ldi R18, 16 // iterate over 16 bits
  122. div1:
  123. lsl R22 // shift left A_L
  124. rol R23
  125. rol R20 // shift left P with carry from A shift
  126. rol R21
  127. sub R20, R24 // Subtract B from P, P <= P - B
  128. sbc R21, R25
  129. brlo div2
  130. ori R22, 1 // Set A[0] = 1
  131. rjmp div3
  132. div2: // negative branch, A[0] = 0 (default after shift), restore P
  133. add R20, R24 // restore old value of P
  134. adc R21, R25
  135. div3:
  136. dec R18
  137. brne div1
  138. finish:
  139. movw R24, R22 // Move result from R22:R23 to R24:R25
  140. end;
  141. {It is a compilerproc (systemh.inc), make an alias for internal use.}
  142. {$ifdef FPC_IS_SYSTEM}
  143. function fpc_div_word(n, z: word): word; external name 'FPC_DIV_WORD';
  144. {$endif FPC_IS_SYSTEM}
  145. {$endif FPC_SYSTEM_HAS_DIV_WORD}
  146. {$ifndef FPC_SYSTEM_HAS_DIV_DWORD}
  147. {$define FPC_SYSTEM_HAS_DIV_DWORD}
  148. // z in Ra, n in Rb, 0 in Rp
  149. function fpc_div_dword(n, z: dword): dword; assembler; nostackframe;
  150. {$ifdef FPC_IS_SYSTEM}[public,alias: 'FPC_DIV_DWORD'];{$endif}
  151. label
  152. start, div1, div2, div3, finish;
  153. asm
  154. // Symbol Name Register(s)
  155. // z (A) dividend R21, R20, R19, R18
  156. // n (B) divisor R25, R24, R23, R22
  157. // p (P) remainder R17, R16, R15, R14
  158. // i counter R26
  159. cp R24, R1
  160. cpc R25, R1
  161. cpc R22, R1
  162. cpc R23, R1
  163. brne .LNonZero
  164. {$ifdef CPUAVR_HAS_JMP_CALL}
  165. call get_pc_addr
  166. {$else CPUAVR_HAS_JMP_CALL}
  167. rcall get_pc_addr
  168. {$endif CPUAVR_HAS_JMP_CALL}
  169. movw R20, R24
  170. {$ifdef CPUAVR_HAS_JMP_CALL}
  171. call get_frame
  172. {$else CPUAVR_HAS_JMP_CALL}
  173. rcall get_frame
  174. {$endif CPUAVR_HAS_JMP_CALL}
  175. movw R18, R24
  176. ldi R22, 200
  177. clr R23
  178. clr R24
  179. clr R25
  180. {$ifdef CPUAVR_HAS_JMP_CALL}
  181. call HandleErrorAddrFrameInd
  182. {$else CPUAVR_HAS_JMP_CALL}
  183. rcall HandleErrorAddrFrameInd
  184. {$endif CPUAVR_HAS_JMP_CALL}
  185. .LNonZero:
  186. push R17
  187. push R16
  188. push R15
  189. push R14
  190. start: // Start of division...
  191. clr R14 // clear remainder
  192. clr R15 // clear remainder
  193. clr R16
  194. clr R17
  195. ldi R26, 32 // iterate over 32 bits
  196. div1:
  197. lsl R18 // shift left A_L
  198. rol R19
  199. rol R20
  200. rol R21
  201. rol R14 // shift left P with carry from A shift
  202. rol R15
  203. rol R16
  204. rol R17
  205. sub R14, R22 // Subtract B from P, P <= P - B
  206. sbc R15, R23
  207. sbc R16, R24
  208. sbc R17, R25
  209. brlo div2
  210. ori R18, 1 // Set A[0] = 1
  211. rjmp div3
  212. div2: // negative branch, A[0] = 0 (default after shift), restore P
  213. add R14, R22 // restore old value of P
  214. adc R15, R23
  215. adc R16, R24
  216. adc R17, R25
  217. div3:
  218. dec R26
  219. brne div1
  220. finish:
  221. movw R22, R18 // Move result from R18:R21 to R22:R25
  222. movw R24, R20
  223. pop R14
  224. pop R15
  225. pop R16
  226. pop R17
  227. end;
  228. {It is a compilerproc (systemh.inc), make an alias for internal use.}
  229. {$ifdef FPC_IS_SYSTEM}
  230. function fpc_div_dword(n, z: dword): dword; external name 'FPC_DIV_DWORD';
  231. {$endif FPC_IS_SYSTEM}
  232. {$endif FPC_SYSTEM_HAS_DIV_DWORD}