在 Assembly 中同时生成 2 个随机整数

Generate 2 random integers simultaneously in Assembly

背景

函数 rollCubes 在西洋双陆棋游戏的每一轮中掷出 2 个立方体。

问题

当运行: rollCubes总是returns 2-2, 4-46-6.

[编辑]

调试时(执行 turbo debugger 中的每一行): 该函数设法生成两个不同的随机整数,但立方体 1 从未滚动.

这让我想知道是什么阻止 rollCubes 在 运行 可执行文件时生成随机整数。

源代码

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

您的代码读取了一些数字 (BIOS.Timer) 并仅保留可能值 {1,2,3} 的一个子集。重复后得到 {2,3,4,5,6}。 你永远不能这样掷 1

一掷骰子应产生一个从 1 到 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

myRandom 过程中的代码是一个 pseudo random number generator,它将在重复自身之前产生 65536 个唯一的 16 位数字。它很快就会输出相同的数字。

当我 运行 上述代码 80 次时(NextRandom 从 0 开始),我得到以下用于掷骰子的数字:

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

这些数字的分布很好:

1 ~ 13
2 ~ 13
3 ~ 16
4 ~ 12
5 ~ 11
6 ~ 15

[编辑]

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.

线性地址 0040h:006Ch 的 BIOS.Timer 标记每 55 毫秒递增一次。这在计算机中是一个很长的时间,并且因为您的 rollCubes 程序快速连续读取此计数器 4 次 ,您几乎总是会收到相同的它的价值。

这是大多数时候发生的情况:

  • 如果TimerTick恰好以00b

    结束
      addAgainHigh:
       00b  reread until it changes to 01b
       01b=1  hicube = 0 + 1 + 1 = 2
      addAgainLow:
       01b=1  locube = 0 + 1 + 1 = 2
    
  • 如果TimerTick恰好以01b

    结束
      addAgainHigh:
       01b=1  hicube = 0 + 1 + 1 = 2
      addAgainLow:
       01b=1  locube = 0 + 1 + 1 = 2
    
  • 如果TimerTick恰好以10b

    结束
      addAgainHigh:
       10b=2  hicube = 0 + 2 + 2 = 4
      addAgainLow:
       10b=2  locube = 0 + 2 + 2 = 4
    
  • 如果TimerTick恰好以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.

确实,快速继承的问题已经不复存在了!从 Turbo Debugger 中执行程序需要时间。 BIOS.Timer线性地址 0040h:006Ch 处的滴答将(很可能)在程序读取之间增加。

When debugging (executing each line inside turbo debugger): the function manages to produce random rolls as should be.

不是真的!正如我最早的回答中所述,您永远不能以这种方式掷出 1。
因为您的程序抛出了零,以下是您的代码可以通过加法组合两个集合 {1,2,3} 产生的所有总和:

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

请注意,这样数字的概率非常不同。