mersenne64.asm 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. %include "defs.asm"
  2. ; ----------------------------- MERSENNE64.ASM ---------------------------
  3. ; Author: Agner Fog
  4. ; Date created: 1998
  5. ; Last modified: 2013-09-13
  6. ; Source URL: www.agner.org/optimize
  7. ; Project: asmlib.zip
  8. ; Language: assembly, NASM/YASM syntax, 64 bit
  9. ; Description:
  10. ; Random Number generator 'Mersenne Twister' type MT11213A (or MT19937)
  11. ;
  12. ;
  13. ; This random number generator is described in the article by
  14. ; M. Matsumoto & T. Nishimura, in:
  15. ; ACM Transactions on Modeling and Computer Simulation,
  16. ; vol. 8, no. 1, 1998, pp. 3-30. See also:
  17. ; http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
  18. ;
  19. ; Initialization:
  20. ; MersRandomInit must be called before the first call to any of the other
  21. ; random number functions. The seed is any 32-bit integer.
  22. ; You may use MersRandomInitByArray instead if you want more
  23. ; than 32 bits for seed. length is the number of integers in seeds[].
  24. ; length must be > 0, there is no upper limit for length.
  25. ;
  26. ; Generating random numbers:
  27. ; MersRandom returns a floating point number in the interval 0 <= x < 1 with
  28. ; a resolution of 32 bits.
  29. ; MersIRandom returns an integer in the interval defined by min and max with
  30. ; a resolution of 32 bits.
  31. ; MersIRandomX returns an integer in the interval defined by min and max with
  32. ; exactly equal probabilities of all values in the interval.
  33. ; MersBRandom returns 32 random bits.
  34. ;
  35. ; Error conditions:
  36. ; If MersRandomInit or MersRandomInitByArray has not been called then MersRandom
  37. ; and MersBRandom keep returning 0, and MersIRandom and MersIRandomX return min.
  38. ; MersIRandom and MersIRandomX return a large negative number if max < min.
  39. ;
  40. ; C++ prototypes in randoma.h:
  41. ; Thread-safe versions:
  42. ; extern "C" void MersRandomInit(void * Pthis, int seed); // Re-seed
  43. ; extern "C" void MersRandomInitByArray(void * Pthis, unsigned int seeds[], int length); // Seed by more than 32 bits
  44. ; extern "C" int MersIRandom (void * Pthis, int min, int max); // Output random integer
  45. ; extern "C" int MersIRandomX(void * Pthis, int min, int max); // Output random integer, exact
  46. ; extern "C" double MersRandom(void * Pthis); // Output random float
  47. ; extern "C" unsigned int MersBRandom(void * Pthis); // Output random bits
  48. ;
  49. ; Single-threaded versions:
  50. ; extern "C" void MersenneRandomInit(int seed); // Re-seed
  51. ; extern "C" void MersenneRandomInitByArray(unsigned int seeds[], int length); // Seed by more than 32 bits
  52. ; extern "C" int MersenneIRandom (int min, int max); // Output random integer
  53. ; extern "C" int MersenneIRandomX(int min, int max); // Output random integer, exact
  54. ; extern "C" double MersenneRandom(); // Output random float
  55. ; extern "C" unsigned int MersenneBRandom(); // Output random bits
  56. ;
  57. ; Copyright (c) 2008-2013 GNU General Public License www.gnu.org/licenses
  58. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  59. default rel
  60. ; structure definition and constants:
  61. %INCLUDE "randomah.asi"
  62. global MersenneRandomInit, MersenneRandomInitD, MersRandomInit
  63. global MersenneRandomInitByArray, MersenneRandomInitByArrayD, MersRandomInitByArray
  64. global MersenneBRandom, MersenneBRandomD, MersBRandom
  65. global MersenneRandom, MersenneRandomD, MersRandom
  66. global MersenneIRandom, MersenneIRandomD, MersIRandom
  67. global MersenneIRandomX, MersenneIRandomXD, MersIRandomX
  68. section .data
  69. align 16
  70. ; Data for single instance of random number generator
  71. MersenneInstance: ISTRUC CRandomMersenneA
  72. IEND
  73. ; Size of structure
  74. MersenneSize equ $ - MersenneInstance
  75. SECTION .CODE ALIGN=16
  76. MersenneRandomInit: ; PROC
  77. %IFDEF UNIX
  78. mov edx, edi ; seed
  79. lea rcx, [MersenneInstance] ; Pthis = point to instance
  80. jmp ?Windows_MersRandomInit
  81. %ENDIF
  82. %IFDEF WINDOWS
  83. MersenneRandomInitD: ; alias
  84. mov edx, ecx ; seed
  85. lea rcx, [MersenneInstance] ; Pthis = point to instance
  86. ;jmp ?Windows_MersRandomInit
  87. %ENDIF
  88. ;MersenneRandomInit ENDP
  89. ; Thread-safe version:
  90. ; extern "C" void MersRandomInit(void * Pthis, int seed); // Re-seed
  91. MersRandomInit: ; PROC
  92. %IFDEF UNIX
  93. ; translate calling convention
  94. mov edx, esi ; seed
  95. mov rcx, rdi ; Pthis
  96. %ENDIF
  97. ; parameters: rcx = Pthis, edx = seed
  98. and rcx, -16 ; align buffer
  99. ?Windows_MersRandomInit:
  100. call Mers_init0 ; initialize mt buffer with seeds
  101. ; Number of premade numbers that are lost in the initialization when the
  102. ; SSE2 implementation makes up to 4 premade numbers at a time:
  103. %IF MERS_N & 3
  104. PREMADELOST equ (MERS_N & 3)
  105. %ELSE
  106. PREMADELOST equ 4
  107. %ENDIF
  108. ; We want the C++ and the assembly implementation to give exactly the same
  109. ; sequence. The C++ version discards 37 random numbers after initialization.
  110. ; The assembly version generates a sequence that is PREMADELOST + 1 numbers
  111. ; behind. Therefore we discard the first 37 + PREMADELOST + 1 numbers if
  112. ; SSE2 is supported, otherwise 37 + 1.
  113. push rbx
  114. mov ebx, 37+PREMADELOST+1
  115. ; CMP dword [rcx+CRandomMersenneA.Instset], 4 ; can we use XMM registers and SSE2 ?
  116. ; jae M110
  117. ; sub ebx, PREMADELOST ; SSE2 not supported
  118. ; mov dword [rcx+CRandomMersenneA.PreInx], 0 ; reset index to premade list
  119. M110: ; loop
  120. M120: call ?Windows_MersBRandom
  121. dec ebx
  122. jnz M120
  123. pop rbx
  124. ret
  125. ;MersRandomInit ENDP
  126. Mers_init0: ; make random seeds from eax and put them into MT buffer
  127. ; Input parameters:
  128. ; rcx points to CRandomMersenneA
  129. ; edx: seed
  130. ; rcx unchanged by procedure
  131. push rdi
  132. ; clear my buffer
  133. push rcx
  134. mov rdi, rcx ; Pthis
  135. add rdi, 16
  136. mov ecx, (MersenneSize - 16) / 4
  137. xor eax, eax
  138. cld
  139. rep stosd
  140. pop rcx ; Pthis
  141. mov edi, edx ; seed
  142. ; initialize CRandomMersenneA structure
  143. mov dword [rcx+CRandomMersenneA.PreInx], 4*4
  144. mov dword [rcx+CRandomMersenneA.Instset], 4
  145. mov eax, MERS_B
  146. mov [rcx+CRandomMersenneA.TMB], eax
  147. mov [rcx+CRandomMersenneA.TMB+4], eax
  148. mov [rcx+CRandomMersenneA.TMB+8], eax
  149. mov [rcx+CRandomMersenneA.TMB+12], eax
  150. mov eax, MERS_C
  151. mov [rcx+CRandomMersenneA.TMC], eax
  152. mov [rcx+CRandomMersenneA.TMC+4], eax
  153. mov [rcx+CRandomMersenneA.TMC+8], eax
  154. mov [rcx+CRandomMersenneA.TMC+12], eax
  155. mov eax, 3FF00000H ; upper dword of 1.0, double precision
  156. mov [rcx+CRandomMersenneA.one+4], eax
  157. mov [rcx+CRandomMersenneA.one+12], eax
  158. mov dword [rcx+CRandomMersenneA.LMASK], LOWER_MASK
  159. mov dword [rcx+CRandomMersenneA.UMASK], UPPER_MASK
  160. mov dword [rcx+CRandomMersenneA.MATA], MERS_A
  161. ; put random numbers into MT buffer
  162. xor eax, eax
  163. M210: mov [rcx+rax*4+CRandomMersenneA.MT], edi
  164. mov edx, edi
  165. shr edi, 30
  166. xor edi, edx
  167. imul edi, 1812433253
  168. inc eax
  169. add edi, eax
  170. cmp eax, MERS_N
  171. jb M210
  172. ; Set index MTI to end of list, (scaled by 4)
  173. ; Round up to multiple of 4 to avoid alignment error
  174. mov dword [rcx+CRandomMersenneA.MTI], ((MERS_N+3) & (-4)) * 4
  175. pop rdi
  176. ret
  177. ; Single threaded version:
  178. ; extern "C" void MersenneRandomInitByArray(unsigned int seeds[], int length);
  179. MersenneRandomInitByArray: ; PROC ; entry for Linux call
  180. %IFDEF UNIX
  181. mov r8d, esi ; length
  182. mov rdx, rdi ; seeds
  183. lea rcx, [MersenneInstance] ; Pthis = point to instance
  184. jmp ?Windows_MersRandomInitByArray
  185. %ENDIF
  186. %IFDEF WINDOWS
  187. MersenneRandomInitByArrayD: ; LABEL NEAR ; alias
  188. mov r8d, edx ; length
  189. mov rdx, rcx ; seeds
  190. lea rcx, [MersenneInstance] ; Pthis = point to instance
  191. jmp ?Windows_MersRandomInitByArray
  192. %ENDIF
  193. ;MersenneRandomInitByArray ENDP
  194. ; Thread-safe version:
  195. ; extern "C" int MersRandomInitByArray(void * Pthis, unsigned int seeds[], int length);
  196. MersRandomInitByArray: ; PROC
  197. %IFDEF UNIX
  198. ; translate calling convention
  199. mov r8d, edx ; length
  200. mov rdx, rsi ; seeds
  201. mov rcx, rdi ; Pthis
  202. %ENDIF
  203. ?Windows_MersRandomInitByArray:
  204. ; parameters: rcx = Pthis, rdx = seeds, r8d = length
  205. and rcx, -16 ; align buffer
  206. push rbx
  207. push rsi
  208. push rdi
  209. push rbp
  210. mov rbx, rdx ; seeds
  211. mov ebp, r8d ; length
  212. mov edx, 19650218
  213. call Mers_init0 ; init0(19650218); (rcx unchanged)
  214. mov r8d, ebp ; r8d = length, ebp = k
  215. test ebp, ebp
  216. jle M380 ; error: length <= 0
  217. xor edi, edi ; j = 0
  218. lea esi, [rdi+1] ; i = 1
  219. cmp ebp, MERS_N
  220. ja M310
  221. mov ebp, MERS_N ; k = max (MERS_N,length)
  222. M310:
  223. ; for (; k; k--) {
  224. M320: mov eax, [rcx+rsi*4-4+CRandomMersenneA.MT] ; mt[i-1]
  225. mov edx, eax
  226. shr eax, 30
  227. xor eax, edx ; mt[i-1] ^ (mt[i-1] >> 30)
  228. imul eax, 1664525 ; * 1664525
  229. xor eax, [rcx+rsi*4+CRandomMersenneA.MT] ; ^ mt[i]
  230. add eax, [rbx+rdi*4] ; + seeds[j]
  231. add eax, edi ; + j
  232. mov [rcx+rsi*4+CRandomMersenneA.MT], eax ; save in mt[i]
  233. inc esi ; i++
  234. inc edi ; j++
  235. cmp esi, MERS_N
  236. jb M330 ; if (i>=MERS_N)
  237. mov eax, [rcx+(MERS_N-1)*4+CRandomMersenneA.MT]; mt[0] = mt[MERS_N-1];
  238. mov [rcx+CRandomMersenneA.MT], eax
  239. mov esi, 1 ; i=1;
  240. M330:
  241. cmp edi, r8d ; length
  242. jb M340 ; if (j>=length)
  243. xor edi, edi ; j = 0;
  244. M340:
  245. dec ebp ; k--
  246. jnz M320 ; first k loop
  247. M350:
  248. mov ebp, MERS_N-1 ; k
  249. M360: mov eax, [rcx+rsi*4-4+CRandomMersenneA.MT] ; mt[i-1]
  250. mov edx, eax
  251. shr eax, 30
  252. xor eax, edx ; mt[i-1] ^ (mt[i-1] >> 30)
  253. imul eax, 1566083941 ; * 1566083941
  254. xor eax, [rcx+rsi*4+CRandomMersenneA.MT] ; ^ mt[i]
  255. sub eax, esi ; - i
  256. mov [rcx+rsi*4+CRandomMersenneA.MT], eax ; save in mt[i]
  257. inc esi ; i++
  258. cmp esi, MERS_N
  259. jb M370 ; if (i>=MERS_N)
  260. mov eax, [rcx+(MERS_N-1)*4+CRandomMersenneA.MT]; mt[0] = mt[MERS_N-1];
  261. mov [rcx+CRandomMersenneA.MT], eax
  262. mov esi, 1 ; i=1;
  263. M370:
  264. dec ebp ; k--
  265. jnz M360 ; second k loop
  266. mov dword [rcx+CRandomMersenneA.MT], 80000000H ; mt[0] = 0x80000000
  267. M380:
  268. mov dword [rcx+CRandomMersenneA.MTI], 0
  269. mov dword [rcx+CRandomMersenneA.PreInx], 0
  270. ; discard first MERS_N random numbers + PREMADELOST+1 to compensate for lag
  271. mov edi, MERS_N + PREMADELOST+1
  272. M391: call ?Windows_MersBRandom
  273. dec edi
  274. jnz M391
  275. pop rbp ; restore registers
  276. pop rdi
  277. pop rsi
  278. pop rbx
  279. ret
  280. ;MersRandomInitByArray ENDP
  281. ; Single threaded version:
  282. ; extern "C" unsigned int MersenneBRandom(); // Output random bits
  283. MersenneBRandom: ; PROC ; entry for both Windows and Linux call
  284. %IFDEF WINDOWS
  285. MersenneBRandomD: ; LABEL NEAR ; alias
  286. %ENDIF
  287. lea rcx, [MersenneInstance] ; Point to instance
  288. jmp ?Windows_MersBRandom
  289. ;MersenneBRandom ENDP
  290. ; Thread-safe version:
  291. ; extern "C" unsigned int MersBRandom(void * Pthis); // Output random bits
  292. MersBRandom: ; PROC
  293. %IFDEF UNIX
  294. mov rcx, rdi ; translate calling convention
  295. %ENDIF
  296. ?Windows_MersBRandom: ; LABEL NEAR ; Label used internally
  297. and rcx, -16 ; align buffer
  298. mov edx, [rcx+CRandomMersenneA.PreInx] ; index into premade numbers
  299. mov eax, [rcx+rdx*1+CRandomMersenneA.PreInt] ; fetch premade random number
  300. add edx, 4
  301. mov [rcx+CRandomMersenneA.PreInx], edx
  302. cmp edx, 4*4
  303. jnb M410
  304. ret ; return premade number
  305. M410:
  306. ; PREMADE list is empty. Make 4 more numbers ready for next call:
  307. mov edx, [rcx+CRandomMersenneA.MTI] ; fetch 4 numbers from MT buffer
  308. movdqa xmm0, oword [rcx+rdx*1+CRandomMersenneA.MT]
  309. %IF TEMPERING ; optional tempering algorithm
  310. movdqa xmm1, xmm0
  311. psrld xmm0, MERS_U
  312. pxor xmm0, xmm1
  313. movdqa xmm1, xmm0
  314. pslld xmm0, MERS_S
  315. pand xmm0, oword [rcx+CRandomMersenneA.TMB]
  316. pxor xmm0, xmm1
  317. movdqa xmm1, xmm0
  318. pslld xmm0, MERS_T
  319. pand xmm0, oword [rcx+CRandomMersenneA.TMC]
  320. pxor xmm0, xmm1
  321. movdqa xmm1, xmm0
  322. psrld xmm0, MERS_L
  323. pxor xmm0, xmm1
  324. %ENDIF ; tempering
  325. ; save four premade integers
  326. movdqa oword [rcx+CRandomMersenneA.PreInt], xmm0
  327. ; premake four floating point numbers
  328. pxor xmm1, xmm1
  329. pxor xmm2, xmm2
  330. punpckldq xmm1, xmm0 ; get first two numbers into bits 32-63 and 96-127
  331. punpckhdq xmm2, xmm0 ; get next two numbers into bits 32-63 and 96-127
  332. psrlq xmm1, 12 ; get bits into mantissa position
  333. psrlq xmm2, 12 ; get bits into mantissa position
  334. por xmm1,oword[rcx+CRandomMersenneA.one] ; set exponent for interval [1,2)
  335. por xmm2,oword[rcx+CRandomMersenneA.one] ; set exponent for interval [1,2)
  336. movdqa oword [rcx+CRandomMersenneA.PreFlt], xmm1 ; store two premade numbers
  337. movdqa oword [rcx+CRandomMersenneA.PreFlt+16],xmm2; store two more premade numbers
  338. mov dword [rcx+CRandomMersenneA.PreInx], 0 ; index to premade numbers
  339. add edx, 4*4 ; increment MTI index into MT buffer by 4
  340. mov [rcx+CRandomMersenneA.MTI], edx
  341. cmp edx, MERS_N*4
  342. jae M420
  343. ret ; return random number in eax
  344. ; MT buffer exhausted. Make MERS_N new numbers ready for next time
  345. M420: ; eax is the random number to return
  346. %IF MERS_N & 3 ; if MERS_N is not divisible by 4
  347. NVALID equ MERS_N & 3 ; only NVALID of the 4 premade numbers are valid
  348. ; Move premade numbers (4-NVALID) positions forward
  349. movdqa xmm0, [rcx+CRandomMersenneA.PreInt]
  350. movdqa xmm1, [rcx+CRandomMersenneA.PreFlt]
  351. movdqa xmm2, [rcx+CRandomMersenneA.PreFlt+16]
  352. movdqu [rcx+CRandomMersenneA.PreInt + (4-NVALID)*4], xmm0
  353. movdqu [rcx+CRandomMersenneA.PreFlt + (4-NVALID)*8], xmm1
  354. %IF NVALID == 3
  355. movq [rcx+CRandomMersenneA.PreFlt+16 + 8], xmm2
  356. %ENDIF
  357. ; save index to first valid premade number
  358. mov [rcx+CRandomMersenneA.PreInx], (4-NVALID)*4
  359. %ENDIF
  360. ; MT buffer is empty. Fill it up
  361. push rbx
  362. movd xmm3, [rcx+CRandomMersenneA.UMASK] ; load constants
  363. movd xmm4, [rcx+CRandomMersenneA.LMASK]
  364. movd xmm5, [rcx+CRandomMersenneA.MATA]
  365. pshufd xmm3, xmm3, 0 ; broadcast constants
  366. pshufd xmm4, xmm4, 0
  367. pshufd xmm5, xmm5, 0
  368. xor rbx, rbx ; kk = 0
  369. mov edx, MERS_M*4 ; km
  370. ; change rcx from pointing to CRandomMersenneA to pointing to CRandomMersenneA.MT
  371. add rcx, CRandomMersenneA.MT
  372. M430: ; kk loop
  373. movdqa xmm2, [rcx+rbx] ; mt[kk]
  374. movd xmm0, dword [rcx+rbx+16]
  375. movdqa xmm1, [rcx+rbx] ; mt[kk]
  376. movss xmm2, xmm0 ; faster than movdqu xmm2,[]
  377. pshufd xmm2, xmm2, 00111001B ; mt[kk+1]
  378. movdqu xmm0, oword [rcx+rdx] ; mt[km]
  379. ;movq xmm0, qword [rcx+rdx] ; mt[km]
  380. ;movhps xmm0, qword [rcx+rdx+8] ; faster than movdqu on older processors
  381. pand xmm1, xmm3 ; mt[kk] & UPPER_MASK
  382. pand xmm2, xmm4 ; mt[kk+1] & LOWER_MASK
  383. por xmm1, xmm2 ; y
  384. movdqa xmm2, xmm1 ; y
  385. pslld xmm1, 31 ; copy bit 0 into all bits
  386. psrad xmm1, 31 ; -(y & 1)
  387. pand xmm1, xmm5 ; & MERS_A
  388. psrld xmm2, 1 ; y >> 1
  389. pxor xmm0, xmm1
  390. pxor xmm0, xmm2
  391. movdqa [rcx+rbx], xmm0 ; result into mt[kk]
  392. cmp ebx, (MERS_N-4)*4
  393. jae M440 ; exit loop when kk past end of buffer
  394. add ebx, 16 ; kk += 4
  395. add rdx, 16 ; km += 4 (signed)
  396. cmp edx, (MERS_N-4)*4
  397. jbe M430 ; skip unless km wraparound
  398. sub rdx, MERS_N*4 ; km wraparound (signed)
  399. movdqu xmm0, [rcx+(MERS_N-4)*4] ; copy end to before begin for km wraparound
  400. movdqa [rcx-4*4], xmm0
  401. movdqa xmm0, [rcx] ; copy begin to after end for kk wraparound
  402. movdqu [rcx+MERS_N*4], xmm0
  403. jmp M430
  404. M440: ; loop finished. discard excess part of last result
  405. ; change ecx back to pointing to CRandomMersenneA
  406. sub rcx, CRandomMersenneA.MT
  407. mov dword [rcx+CRandomMersenneA.MTI], 0
  408. pop rbx
  409. ret ; random number is still in eax
  410. ;MersBRandom ENDP
  411. ; Single threaded version:
  412. ; extern "C" unsigned int MersenneRandom(); // Get floating point random number
  413. MersenneRandom: ; PROC ; entry for both Windows and Linux call
  414. %IFDEF WINDOWS
  415. MersenneRandomD: ; alias
  416. lea rcx, [MersenneInstance] ; Point to instance
  417. ; continue in next function
  418. %ENDIF
  419. %IFDEF UNIX
  420. lea rdi, [MersenneInstance] ; Point to instance
  421. ; continue in next function
  422. %ENDIF
  423. ; Thread-safe version:
  424. ; extern "C" double MersRandom(void * Pthis); // Get floating point random number
  425. MersRandom:
  426. %IFDEF UNIX
  427. mov rcx, rdi ; translate calling convention
  428. %ENDIF
  429. mov edx, [rcx+CRandomMersenneA.PreInx] ; index into premade numbers
  430. movsd xmm0, [rcx+rdx*2+CRandomMersenneA.PreFlt] ; fetch premade floating point random number
  431. subsd xmm0, [rcx+CRandomMersenneA.one] ; subtract 1.0
  432. movsd [rcx+CRandomMersenneA.TmpFlt], xmm0 ; store random number
  433. call ?Windows_MersBRandom ; prepare next random number
  434. movsd xmm0, [rcx+CRandomMersenneA.TmpFlt] ; recall random number
  435. ret
  436. ;MersenneRandom ENDP
  437. ; Single threaded version:
  438. ; extern "C" unsigned int MersenneIRandom(int min, int max); // Get integer random number in desired interval
  439. MersenneIRandom: ; PROC
  440. %IFDEF UNIX
  441. push rsi ; max
  442. push rdi ; min
  443. lea rcx, [MersenneInstance] ; Pthis = point to instance
  444. jmp MersIRandom_max_min_on_stack
  445. %ENDIF
  446. %IFDEF WINDOWS
  447. MersenneIRandomD: ; Alias
  448. push rdx ; max
  449. push rcx ; min
  450. lea rcx, [MersenneInstance] ; Pthis = point to instance
  451. jmp MersIRandom_max_min_on_stack
  452. %ENDIF
  453. ;MersenneIRandom ENDP
  454. ; Thread-safe version:
  455. ; extern "C" int MersIRandom(void * Pthis, int min, int max); // Get integer random number in desired interval
  456. MersIRandom: ; PROC
  457. %IFDEF UNIX
  458. ; translate calling convention
  459. mov r8d, edx ; max
  460. mov edx, esi ; min
  461. mov rcx, rdi ; Pthis
  462. %ENDIF
  463. push r8 ; max
  464. push rdx ; min
  465. MersIRandom_max_min_on_stack:
  466. call ?Windows_MersBRandom ; random bits
  467. pop rcx ; min
  468. pop rdx ; max
  469. sub edx, ecx
  470. js short M720 ; max < min
  471. add edx, 1 ; interval = max - min + 1
  472. mul edx ; multiply random number by interval and truncate
  473. lea eax, [rdx+rcx] ; add min
  474. ret
  475. M720: mov eax, 80000000H ; error exit
  476. ret
  477. ;MersIRandom ENDP
  478. ; Single threaded version:
  479. ; extern "C" unsigned int MersenneIRandomX(int min, int max); // Get integer random number in desired interval
  480. MersenneIRandomX: ; PROC
  481. %IFDEF UNIX
  482. mov r8d, esi ; max
  483. mov edx, edi ; min
  484. lea rcx, [MersenneInstance] ; Pthis = point to instance
  485. jmp ?Windows_MersIRandomX
  486. %ENDIF
  487. %IFDEF WINDOWS
  488. MersenneIRandomXD: ; alias
  489. mov r8d, edx ; max
  490. mov edx, ecx ; min
  491. lea rcx, [MersenneInstance] ; Pthis = point to instance
  492. jmp ?Windows_MersIRandomX
  493. %ENDIF
  494. ;MersenneIRandomX ENDP
  495. ; Thread-safe version:
  496. ; extern "C" int MersIRandomX(void * Pthis, int min, int max); // Get integer random number in desired interval
  497. MersIRandomX: ; PROC
  498. %IFDEF UNIX
  499. ; translate calling convention
  500. mov r8d, edx ; max
  501. mov edx, esi ; min
  502. mov rcx, rdi ; Pthis
  503. %ENDIF
  504. ?Windows_MersIRandomX:
  505. ; parameters: rcx = Pthis, edx = min, r8d = max
  506. and rcx, -16 ; align buffer
  507. push rdi
  508. mov edi, r8d ; max
  509. sub edi, edx ; max - min
  510. jle short M830 ; max <= min (signed)
  511. inc edi ; interval = max - min + 1
  512. push rdx ; save min
  513. ; if (interval != LastInterval) {
  514. cmp edi, [rcx+CRandomMersenneA.LastInterval]
  515. je M810
  516. ; RLimit = uint32(((uint64)1 << 32) / interval) * interval - 1;}
  517. xor eax, eax ; 0
  518. lea edx, [rax+1] ; 1
  519. div edi ; (would give overflow if interval = 1)
  520. mul edi
  521. dec eax
  522. mov [rcx+CRandomMersenneA.RLimit], eax
  523. mov [rcx+CRandomMersenneA.LastInterval], edi
  524. M810:
  525. M820: ; do { // Rejection loop
  526. call ?Windows_MersBRandom ; random bits (rcx is preserved)
  527. ; longran = (uint64)BRandom() * interval;
  528. mul edi
  529. ; } while (remainder > RLimit);
  530. cmp eax, [rcx+CRandomMersenneA.RLimit]
  531. ja M820
  532. ; return (int32)iran + min
  533. pop rax ; min
  534. add eax, edx
  535. pop rdi
  536. ret
  537. M830: jl M840
  538. ; max = min. Return min
  539. mov eax, edx
  540. pop rdi
  541. ret ; max = min exit
  542. M840: ; max < min: error
  543. mov eax, 80000000H ; error exit
  544. pop rdi
  545. ret
  546. ;MersIRandomX ENDP