The function rollCubes
rolls 2 cubes within each turn of a backgammon game.
When running: rollCubes
always returns 2-2, 4-4
or 6-6
.
When debugging (executing each line inside turbo debugger
): the function manages to produce two different random integers, but the cube 1
is never rolled.
It makes me wonder what prevents rollCubes
from producing random integers when running the executable.
proc rollCubes
xor cx, cx
mov [hicube], 0
mov [locube], 0
mov cx, 2
addAgainHigh:
; Random number [0, 3] --> two rolls [0, 6]
mov ax, 40h
mov es, ax
mov ax, es:6Ch ;Random number
and al, 00000011b
cmp al, 0 ;Make sure al isn't 0
je addAgainHigh ;If 0, roll again
add [hicube], al ;add to cube value
loop addAgainHigh
mov cx, 2
addAgainLow:
; Random number [0, 3] --> two rolls [0, 6]
mov ax, 40h
mov es, ax
mov ax, es:6Ch
and al, 00000011b
cmp al, 0
je addAgainLow
add [locube], al
loop addAgainLow
mov al, [locube]
mov ah, [hicube]
ret
endp rollCubes
Your code reads some number (BIOS.Timer) and keeps only a subset of the possible values {1,2,3}. After repeating you have {2,3,4,5,6}. You can never roll 1 this way
A roll of a dice should produce a number from 1 to 6.
call myRandom ; -> AX = [0,65535]
xor dx, dx
mov cx, 6
div cx ; -> Remainder DX = [0,5]
inc dx ; -> Dice is [1,6]
...
; IN () OUT (ax)
proc myRandom
imul ax, [NextRandom], 25173
add ax, 13849
xor ax, 62832
mov [NextRandom], ax
ret
endp myRandom
The code in this myRandom proc is a pseudo random number generator that will produce 65536 unique 16-bit numbers before it will repeat itself. No sooner will it output the same number.
When I run the above code 80 times (NextRandom starts at 0), I get the following numbers for rolling the dice:
4,5,6,1,2,3,2,3,4,3, 2,3,6,5,6,3,4,3,4,1
6,5,6,5,4,3,6,1,2,5, 4,5,2,3,6,5,2,1,6,3
6,3,6,3,2,3,2,1,2,5, 2,1,2,3,4,1,6,5,4,1
4,5,4,5,4,1,2,1,2,3, 6,3,6,1,4,1,6,3,6,1
The distribution of these numbers is fine:
1 ~ 13
2 ~ 13
3 ~ 16
4 ~ 12
5 ~ 11
6 ~ 15
[edit]
When running: rollCubes always returns 2-2, 4-4 or 6-6. It makes me wonder what prevents rollCubes from producing randoms when running the executable.
The BIOS.TimerTick at linear address 0040h:006Ch increments every 55 ms. This is a very long time in computers, and because your rollCubes procedure reads this counter 4 times in rapid succession, you will nearly always receive the same value from it.
This is what happens most of the time:
If TimerTick happens to end with 00b
addAgainHigh:
00b reread until it changes to 01b
01b=1 hicube = 0 + 1 + 1 = 2
addAgainLow:
01b=1 locube = 0 + 1 + 1 = 2
If TimerTick happens to end with 01b
addAgainHigh:
01b=1 hicube = 0 + 1 + 1 = 2
addAgainLow:
01b=1 locube = 0 + 1 + 1 = 2
If TimerTick happens to end with 10b
addAgainHigh:
10b=2 hicube = 0 + 2 + 2 = 4
addAgainLow:
10b=2 locube = 0 + 2 + 2 = 4
If TimerTick happens to end with 11b
addAgainHigh:
11b=3 hicube = 0 + 3 + 3 = 6
addAgainLow:
11b=3 locube = 0 + 3 + 3 = 6
When debugging (executing each line inside turbo debugger): the function manages to produce random rolls as should be.
Indeed, there's no longer the problem of rapid succession! Executing the program from within Turbo Debugger takes time. The BIOS.TimerTick at linear address 0040h:006Ch will (very probably) have incremented in between reads from your program.
When debugging (executing each line inside turbo debugger): the function manages to produce random rolls as should be.
Not really! As stated in my earliest answer, you can never roll 1 this way.
Because your program throws out the zero, the following are all the sums your code can produce combining both sets {1,2,3} through addition:
1 + 1 = 2
1 + 2 = 3
1 + 3 = 4
2 + 1 = 3
2 + 2 = 4
2 + 3 = 5
3 + 1 = 4
3 + 2 = 5
3 + 3 = 6
Do notice that the numbers have very different probabilities this way.