x86 Irvine Assembly jmp + cmp和条件循环 - 掷骰子游戏

时间:2017-11-14 04:15:11

标签: assembly x86

汇编是一种有趣的语言学习。我还有很大的提升空间。我试图做一个简单的掷骰子游戏,应该根据用户输入随机掷骰子x次。我的代码如下:

TITLE Program Template          (main.asm)

INCLUDE Irvine32.inc

.data
diceOne                     DWORD ? 
diceTwo                     DWORD ?
win                         DWORD 7, 11
lose                        DWORD 2, 3, 12
mark                        DWORD 4, 5, 6, 8, 9, 10
markCounter                 DWORD ?
userInput                   BYTE 'Enter integer: ', 0
numRolls                    DWORD ?
printWon                    BYTE 'Won: ', 0
wonCounter                  DWORD ?
printWin                    BYTE ' You win!', 0
printLost                   BYTE 'Lost: ', 0
lostCounter                 DWORD ?
printLose                   BYTE ' You lose!', 0
printTotal                  BYTE 'Total: ', 0
space                       DWORD ' ', 0
printPlus                   BYTE ' + ', 0
printMark                   BYTE ' Mark ', 0

.code
main PROC
    call randomize                      ; set random seed
    mov ecx, 6                          ; counter set to 6
    mov edx, offset userInput           ; print string
    call writeString
    call readInt                        ; read input
    mov numRolls, eax                   ; store input in variable
    mov ecx, numRolls                   
    mov eax, 0
    mov edi, offset win

    call gamesRolled

    exit
main ENDP
;number of games rolled based on user input
gamesRolled PROC uses eax ecx

DICEROLLS: 
    call crlf
                                ;diceOne roll
    mov eax, 5                  ;move value into eax to pass as parameter for randomrange
    call randomRange            ;randomrange stored in eax 0-5
    inc eax
    mov diceOne, eax            ;mov the value of randomrange into variable
    call writeDec       
    push edx                    ;push edx off stack to print space 
    mov edx, OFFSET printPlus
    call writeString
    pop edx
                                ;diceTwo roll
    mov eax, 6
    call randomRange
    inc eax
    mov diceTwo, eax
    call writeDec


    add eax, diceOne            ; add diceOne roll to diceTwo roll
    cmp eax, win                ; comp eax value to win
    je wins
    cmp eax, lose
    je losses
    cmp eax, mark
    je marks
    LOOP DICEROLLS

    ret
gamesRolled ENDP

wins PROC uses edi ecx
    mov edi, offset win
    mov ecx, lengthof win
    cmp eax, [edi]
    add edi, type win
    push edx
    mov edx, offset printWin
    call writeString
    pop edx
    jmp gamesRolled
    ret

wins ENDP

losses PROC uses edi ecx
    mov edi, offset lose
    mov ecx, lengthof lose
    cmp eax, [edi]
    add edi, type lose
    push edx
    mov edx, offset printLose
    call writeString
    pop edx
    jmp gamesRolled
    ret
losses ENDP

marks PROC uses edi ecx
    mov edi, offset lose
    mov ecx, lengthof lose
    cmp eax, [edi]
    add edi, type lose

    push edx
    mov edx, offset printMark
    call writeString    
    pop edx
    jmp gamesRolled
    ret
marks ENDP

END main

从我看到的例子和解释看起来似乎很简单。我正在使用cmp / je像if语句一样工作,同时循环播放骰子。这个想法是让胜利或损失或标记增加并打印出来。没有cmp / je部分,它可以正常工作。我可以打印随机骰子卷,但是,一旦我开始尝试cmp / je,我会得到一些奇怪的冻结,有时会崩溃。我试图学习如何在装配中更好地编码。你能不能帮助我理解为什么这不是我想象的那样。我想如果我比较eax寄存器,其中包含diceOne + diceTwo的总和它应该跳到其他函数,并且在那些函数中edi应该像打印一个计数器的计数器表示胜利,失败或标记的字符串。例如:

Enter integer: 5
2 + 3 Mark
1 + 5 Mark
5 + 6 You win!
5 + 1 Mark
2 + 6 Mark
Wins: 1 Losses: 0 Marks: 4

我还没到达最后一部分,因为我遇到了代码问题。感谢任何帮助。我不知所措。

这是我现在得到的实际结果:

Enter integer: 5

3 + 2
3 + 5
2 + 5 You win!
3 + 6
5 + 5

1 个答案:

答案 0 :(得分:0)

您当前的版本:

第一次随机通话仅为0-4。

cmp eax, win:您使用MASM语法,因此在英特尔语法中为cmp eax,[mem32],将eax与来自7"的值win进行比较阵列&#34 ;.然后je wins将在wins:处的代码处跳转结果7,并且那将执行一些无用的指针内容,将ecx重置为2,显示消息并跳回主卷循环。

类似于丢失/标记"数组",第一个元素(2表示丢失,4表示标记)被测试,当相等时,显示丢失/标记消息,带有一些无用的东西,重置ecx并直接回到滚动。

仅对其他值(3,5,6,8,9,10,11(由于随机错误而丢失12))执行loop,在ecx中向下计数总循环。

你应该很容易在调试器中看到这种行为(甚至发现我的描述有任何差异,如果我在脑子里做了一些错误,因为我的"仿真和#34; CPU有点受限且只是关于如果您单步执行指令并在每个步骤后检查机器状态,则速度为~2Hz)。在没有调试器的情况下进行汇编编程就像试图组装机器人蒙上眼睛一样。你可以,但它比100倍更难,所以在继续进行汇编学习之前,要大力投入学习如何有效地使用调试器。

有关代码的一些建议:

总的来说,你有点复杂,否则我会这样做,我会在数据中定义表(而不是win/lose/mark数组):

;             2L  3L  4M  5M  6M  7W  8M  9M 10M 11W 12L
outcome BYTE   1,  1,  2,  2,  2,  0,  2,  2,  2,  0,  1
outcomeMsgs:
    DWORD  OFFSET printWin, OFFSET printLose, OFFSET printMark
totalScore DWORD 0, 0, 0    ; wins / losses / marks

然后在显示第二次投掷后的代码中,并且在eax投掷总和中,您可以选择结果:

    ; decide what is the outcome of throws
    movzx   ebx,byte ptr [outcome + eax - 2]
      ; ebx = 0/1/2 = win/lose/mark
    ; display outcome message
    mov     edx,[outcomeMsgs + ebx*4]
    call    writeString
    ; add to total score
    inc     dword ptr [totalScore + ebx*4]
    ; do ecx-many rolls
    dec     ecx
    jnz     DICEROLLS
    ; ending sequence
    ; for example, display total losses
    mov     edx,OFFSET printLost
    call    writeString
    mov     eax,[totalScore+4]
    call    writeDec
    ret

不幸的是,如果你想对cmp数组中的值进行win/...运算,那么这就完全避免了,因此教育价值很小。您可能应该单独尝试编写该数组测试,以获得更多技能......在您学会首先使用调试器之后,您可以实际检查代码的真正用途。