Если вы пишете программы для Win32, вы не можете вызывать службы BIOS и DOS, такие как Int 1ah
, Int 10h
, Int 21h
и т. Д. Это приведет к сбою приложения, поскольку программы Win32 не имеют доступа к этим службам.
Базовые rand
и srand
в библиотеке Windows C (MSVCRT.LIB) основаны на генераторе псевдослучайных чисел с линейным конгруэнтным генератором (LCG) (PRNG). Эта формула основывается на начальном значении для установки начального состояния PRNG. Начальное состояние при выполнении вашей программы всегда будет одинаковым при каждом перезапуске программы. Каждый вызов rand
будет воспроизводить псевдослучайное число, но номера будут иметь одинаковую последовательность при каждом запуске программы.
srand
можно использовать для изменения начального значения PRNG. Изменение начального значения приведет к изменению чисел rand
, но они всегда будут иметь одинаковую последовательность чисел с одним и тем же начальным числом. Вам нужен механизм для установки начального значения на другое значение при каждом запуске программы. Вы можете использовать функцию C library time
с параметром NULL (0), чтобы получить количество секунд с полуночи 1 января 1970 года. Это значение должно отличаться, пока Ваша программа не запускается так быстро, как она выполняется в течение одной секунды. Как правило, это достаточно хорошо.
Затем вы можете передать значение, возвращаемое time(0)
в EAX, на srand
, чтобы установить начальное значение. Вызывайте srand
только один раз при запуске вашей программы С этого момента вы сможете звонить rand
, чтобы получить новое случайное число. rand
возвращает значение от 0 до RAND_MAX, а RAND_MAX - 32767.
Этот пример кода выполняет srand(time(0))
для инициализации начального числа, а затем циклически повторяет 10 раз, печатая другое случайное число, полученное при вызовах rand
. Каждый раз, когда вы запускаете программу, результат должен отличаться.
.386
.model flat, C
includelib msvcrt.lib
extern exit: proc
extern printf: proc
extern rand: proc
extern srand: proc
extern time: proc
.data
decimal_format DB "%d", 0ah, 0
; Ensure string is NUL(0) terminated
.code
main PROC
push ebx ; Save callee saved (non-volatile) registers that we use.
; EBX, EBP, ESI, EDI, ESP are non-volatile. For each
; one we clobber we must save it and restore it before
; returning from `main`
push 0
call time ; EAX=time(0)
add esp, 4
push eax ; Use time as seed
call srand ; srand(time(0))
add esp, 4
mov ebx, 10 ; Loop 10 times
loopit:
call rand ; Get a random number between 0 and 32767 into EAX
push eax
push offset decimal_format
call printf ; Print the random number
add esp,8
dec ebx
jnz loopit ; Loop until the counter EBX reaches 0
pop ebx ; Restore callee saved registers
xor eax, eax ; Return 0 from our program
ret
main ENDP
END
Некоторые другие важные изменения. Я использую соглашение о вызовах C (CDECL) (через .model flat, C
), которое автоматически обрабатывает оформление main PROC
подчеркиванием в 32-битном коде. Я также изменил start
на main
и изменил end start
на end
. Мы также не хотим использовать end main
, потому что эта директива сделает main
точкой входа в наш код и пропустит инициализацию C во время выполнения, которую обычно необходимо выполнить до того, как main
будет называется. Невозможность вызвать инициализацию C во время выполнения может привести к неожиданной работе библиотечной функции C или к аварийному завершению работы.
Когда код закончится, я делаю ret
, чтобы вернуться к C коду запуска, который выйдет для нас. Код также сохраняет энергонезависимые (сохраняемые) регистры. См. Microsoft 32-битное соглашение о вызовах CDECL для получения дополнительной информации.