int64_div.g 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. # This file is part of asmc, a bootstrapping OS with minimal seed
  2. # https://gitlab.com/giomasce/asmc
  3. # This file is based on the 64-bit division procedure implemented in
  4. # the Pintos project, available at [1]. The code was translated from C
  5. # to G by Giovanni Mascellani <gio@debian.org>, with little
  6. # modifications to better fit G syntax, and is left under the same
  7. # license terms as the original file. The original file is licensed
  8. # under the following license (avaiable at [2]):
  9. # Copyright © 2004, 2005, 2006 Board of Trustees, Leland Stanford
  10. # Jr. University. All rights reserved.
  11. # Permission is hereby granted, free of charge, to any person
  12. # obtaining a copy of this software and associated documentation files
  13. # (the "Software"), to deal in the Software without restriction,
  14. # including without limitation the rights to use, copy, modify, merge,
  15. # publish, distribute, sublicense, and/or sell copies of the Software,
  16. # and to permit persons to whom the Software is furnished to do so,
  17. # subject to the following conditions:
  18. # The above copyright notice and this permission notice shall be
  19. # included in all copies or substantial portions of the Software.
  20. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  24. # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  25. # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  26. # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  27. # SOFTWARE.
  28. # [1] https://www.cs.usfca.edu/~benson/cs326/pintos/pintos/src/lib/arithmetic.c
  29. # [2] https://web.stanford.edu/class/cs140/projects/pintos/pintos_14.html
  30. fun nlz 1 {
  31. $x
  32. @x 0 param = ;
  33. $n
  34. @n 0 = ;
  35. if x 0xffff0000 & ! {
  36. @n n 16 + = ;
  37. @x x 16 << = ;
  38. }
  39. if x 0xff000000 & ! {
  40. @n n 8 + = ;
  41. @x x 8 << = ;
  42. }
  43. if x 0xf0000000 & ! {
  44. @n n 4 + = ;
  45. @x x 4 << = ;
  46. }
  47. if x 0xc0000000 & ! {
  48. @n n 2 + = ;
  49. @x x 2 << = ;
  50. }
  51. if x 0x80000000 & ! {
  52. @n n 1 + = ;
  53. @x x 1 << = ;
  54. }
  55. n ret ;
  56. }
  57. fun i64_udiv 2 {
  58. $n
  59. $d
  60. @n 1 param = ;
  61. @d 0 param = ;
  62. # Just to be sure, assert that the denominator is not zero
  63. d ** 0 != d 4 + ** 0 != || "i64_udiv: denominator is zero" assert_msg ;
  64. if d 4 + ** 0 == {
  65. # The denominator is only 32 bits, so we can basically use the
  66. # division 64 by 32 already implemented in the processor, except
  67. # that we have to check that there is no overflow (which would
  68. # cause a trap). See the original file for proof of correctness.
  69. $n1
  70. $d0
  71. @n1 n 4 + ** = ;
  72. @d0 d ** = ;
  73. # Mod the numerator upper dword by the denominator
  74. n 4 + n1 d0 %u = ;
  75. # Do the 64 by 32 division
  76. n d0 i64_udiv_64_by_32 ;
  77. # Add the correction term to the upper dword
  78. n 4 + n 4 + ** n1 d0 /u + = ;
  79. } else {
  80. # The denominator has more than 32 bits. The trick here is
  81. # basically to discard the least significant bits to make it only
  82. # 32 bits. This (1) causes a further division by some power of 2,
  83. # which is easy to correct, and (2) it might introduce some
  84. # error. However, by the analysis in [3] (see "Unsigned Doubleword
  85. # Division"), the only bad thing that can happen is that the
  86. # result is one unit too large. So we test if there is need to fix
  87. # it (again, see [3] for the details and proofs).
  88. # [3] http://www.hackersdelight.org/revisions.pdf
  89. $cmp_
  90. $cmp
  91. @cmp n i64_copy ;
  92. @cmp d i64_ul ;
  93. if cmp {
  94. n 0 i64_from_u32 ;
  95. } else {
  96. $d1
  97. @d1 d 4 + ** = ;
  98. $s
  99. @s d1 nlz = ;
  100. $tmp1_
  101. $tmp1
  102. $tmp2
  103. $tmp2_
  104. $one
  105. $one_
  106. @one 1 i64_from_u32 ;
  107. @tmp1 s i64_from_u32 ;
  108. $dcopy_
  109. $dcopy
  110. @dcopy d i64_copy ;
  111. @dcopy @tmp1 i64_shl ;
  112. $q_
  113. $q
  114. @q n i64_copy ;
  115. @q @one i64_shr ;
  116. @q dcopy_ i64_udiv_64_by_32 ;
  117. @tmp1 31 s - i64_from_u32 ;
  118. @q @tmp1 i64_shr ;
  119. @q @one i64_sub ;
  120. @tmp1 @q i64_copy ;
  121. @tmp1 d i64_mul ;
  122. @tmp2 @n i64_copy ;
  123. @tmp2 @tmp1 i64_sub ;
  124. @tmp2 d i64_ul ;
  125. if tmp2 ! {
  126. @q @one i64_add ;
  127. }
  128. n @q i64_copy ;
  129. }
  130. }
  131. }
  132. fun i64_sdiv 2 {
  133. $n
  134. $d
  135. @n 1 param = ;
  136. @d 0 param = ;
  137. $sign
  138. @sign 0 = ;
  139. $tmp1_
  140. $tmp1
  141. $tmp2_
  142. $tmp2
  143. @tmp1 n i64_copy ;
  144. if tmp1_ 0x80000000 & {
  145. @tmp1 i64_neg ;
  146. @sign sign 1 ^ = ;
  147. }
  148. @tmp2 d i64_copy ;
  149. if tmp2_ 0x80000000 & {
  150. @tmp2 i64_neg ;
  151. @sign sign 1 ^ = ;
  152. }
  153. # If one of the operand already has the minimum possible value, it
  154. # does not become positive by negation, but it becomes the right
  155. # thing anyway
  156. tmp1_ 0x80000000 & ! tmp1_ 0x80000000 == || "i64_sdiv: error 1" assert_msg ;
  157. tmp2_ 0x80000000 & ! tmp2_ 0x80000000 == || "i64_sdiv: error 2" assert_msg ;
  158. @tmp1 @tmp2 i64_udiv ;
  159. if sign {
  160. @tmp1 i64_neg ;
  161. }
  162. n @tmp1 i64_copy ;
  163. }
  164. fun i64_umod 2 {
  165. $n
  166. $d
  167. @n 1 param = ;
  168. @d 0 param = ;
  169. $tmp_
  170. $tmp
  171. @tmp n i64_copy ;
  172. @tmp d i64_udiv ;
  173. @tmp d i64_mul ;
  174. n @tmp i64_sub ;
  175. }
  176. fun i64_smod 2 {
  177. $n
  178. $d
  179. @n 1 param = ;
  180. @d 0 param = ;
  181. $tmp_
  182. $tmp
  183. @tmp n i64_copy ;
  184. @tmp d i64_sdiv ;
  185. @tmp d i64_mul ;
  186. n @tmp i64_sub ;
  187. }
  188. fun int64_test_one_div 2 {
  189. $n
  190. $d
  191. @n 1 param = ;
  192. @d 0 param = ;
  193. # "n_up: " log ;
  194. # n 4 + ** itoa log ;
  195. # "\n" log ;
  196. # "n: " log ;
  197. # n ** itoa log ;
  198. # "\n" log ;
  199. # "d_up: " log ;
  200. # d 4 + ** itoa log ;
  201. # "\n" log ;
  202. # "d: " log ;
  203. # d ** itoa log ;
  204. # "\n" log ;
  205. # Do unsigned division and mod
  206. $tmp1_
  207. $tmp1
  208. $tmp2_
  209. $tmp2
  210. @tmp1 n i64_copy ;
  211. @tmp2 n i64_copy ;
  212. @tmp1 d i64_udiv ;
  213. @tmp2 d i64_umod ;
  214. # "div_up: " log ;
  215. # tmp1_ itoa log ;
  216. # "\n" log ;
  217. # "div: " log ;
  218. # tmp1 itoa log ;
  219. # "\n" log ;
  220. # "mod_up: " log ;
  221. # tmp2_ itoa log ;
  222. # "\n" log ;
  223. # "mod: " log ;
  224. # tmp2 itoa log ;
  225. # "\n" log ;
  226. # Check that mod is smaller then denominator
  227. $tmp3_
  228. $tmp3
  229. @tmp3 @tmp2 i64_copy ;
  230. @tmp3 d i64_ul ;
  231. tmp3 "int64_test_one_div: mod is not smaller than denominator after unsigned division" assert_msg ;
  232. # Check that mod + div * d equals n
  233. @tmp3 @tmp1 i64_copy ;
  234. @tmp3 d i64_mul ;
  235. @tmp3 @tmp2 i64_add ;
  236. @tmp3 n i64_eq ;
  237. tmp3 "int64_test_one_div: unsigned division did not work" assert_msg ;
  238. # Do signed division and mod
  239. $tmp1_
  240. $tmp1
  241. $tmp2_
  242. $tmp2
  243. @tmp1 n i64_copy ;
  244. @tmp2 n i64_copy ;
  245. @tmp1 d i64_sdiv ;
  246. @tmp2 d i64_smod ;
  247. # Check that the division sign is correct
  248. if tmp1 0 != tmp1_ 0 != || {
  249. tmp1_ 0x80000000 & n 4 + ** 0x80000000 & d 4 + ** 0x80000000 & ^ == "int64_test_one_div: division sign is wrong" assert_msg ;
  250. }
  251. # Check that mod is smaller then denominator
  252. $tmp3_
  253. $tmp3
  254. @tmp3 @tmp2 i64_copy ;
  255. @tmp3 d i64_l ;
  256. tmp3 "int64_test_one_div: mod is not smaller than denominator after signed division" assert_msg ;
  257. # Check that mod is smaller then denominator
  258. $tmp3_
  259. $tmp3
  260. @tmp3 @tmp2 i64_copy ;
  261. @tmp3 i64_neg ;
  262. @tmp3 d i64_l ;
  263. tmp3 "int64_test_one_div: minus mod is not smaller than denominator after signed division" assert_msg ;
  264. # Check that mod + div * d equals n
  265. @tmp3 @tmp1 i64_copy ;
  266. @tmp3 d i64_mul ;
  267. @tmp3 @tmp2 i64_add ;
  268. @tmp3 n i64_eq ;
  269. tmp3 "int64_test_one_div: signed division did not work" assert_msg ;
  270. }
  271. fun int64_test_div 0 {
  272. $x_
  273. $x
  274. $y_
  275. $y
  276. @x_ 0x00000000 = ;
  277. @x 0x47295583 = ;
  278. @y_ 0x110 = ;
  279. @y 0x2328 = ;
  280. @x @y int64_test_one_div ;
  281. @x_ 0x00000000 = ;
  282. @x 0x47295583 = ;
  283. @y_ 0x1101223 = ;
  284. @y 0x2328 = ;
  285. @x @y int64_test_one_div ;
  286. @x_ 0x00000000 = ;
  287. @x 0x47295583 = ;
  288. @y_ 0x0 = ;
  289. @y 0x2328 = ;
  290. @x @y int64_test_one_div ;
  291. @x_ 0x00000000 = ;
  292. @x 0x47295583 = ;
  293. @y_ 0x0 = ;
  294. @y 0x1 = ;
  295. @x @y int64_test_one_div ;
  296. @x_ 0x10000000 = ;
  297. @x 0x47295583 = ;
  298. @y_ 0x110 = ;
  299. @y 0x2328 = ;
  300. @x @y int64_test_one_div ;
  301. @x_ 0x10000000 = ;
  302. @x 0x47295583 = ;
  303. @y_ 0x1101223 = ;
  304. @y 0x2328 = ;
  305. @x @y int64_test_one_div ;
  306. @x_ 0x10000000 = ;
  307. @x 0x47295583 = ;
  308. @y_ 0x0 = ;
  309. @y 0x2328 = ;
  310. @x @y int64_test_one_div ;
  311. @x_ 0x10000000 = ;
  312. @x 0x47295583 = ;
  313. @y_ 0x0 = ;
  314. @y 0x1 = ;
  315. @x @y int64_test_one_div ;
  316. @x_ 0xf0000000 = ;
  317. @x 0x47295583 = ;
  318. @y_ 0x110 = ;
  319. @y 0x2328 = ;
  320. @x @y int64_test_one_div ;
  321. @x_ 0xf0000000 = ;
  322. @x 0x47295583 = ;
  323. @y_ 0x1101223 = ;
  324. @y 0x2328 = ;
  325. @x @y int64_test_one_div ;
  326. @x_ 0xf0000000 = ;
  327. @x 0x47295583 = ;
  328. @y_ 0x0 = ;
  329. @y 0x2328 = ;
  330. @x @y int64_test_one_div ;
  331. @x_ 0xf0000000 = ;
  332. @x 0x47295583 = ;
  333. @y_ 0x0 = ;
  334. @y 0x1 = ;
  335. @x @y int64_test_one_div ;
  336. @x_ 0xffffffff = ;
  337. @x 0x47295583 = ;
  338. @y_ 0x110 = ;
  339. @y 0x2328 = ;
  340. @x @y int64_test_one_div ;
  341. @x_ 0xffffffff = ;
  342. @x 0x47295583 = ;
  343. @y_ 0x1101223 = ;
  344. @y 0x2328 = ;
  345. @x @y int64_test_one_div ;
  346. @x_ 0xffffffff = ;
  347. @x 0x47295583 = ;
  348. @y_ 0x0 = ;
  349. @y 0x2328 = ;
  350. @x @y int64_test_one_div ;
  351. @x_ 0xffffffff = ;
  352. @x 0x47295583 = ;
  353. @y_ 0x0 = ;
  354. @y 0x1 = ;
  355. @x @y int64_test_one_div ;
  356. "Tests for int64 division successfully passed!\n" log ;
  357. }