【问题标题】:Assembly: Issues with Indexing装配:索引问题
【发布时间】:2015-11-11 02:42:24
【问题描述】:

我无法弄清楚如何在我的循环中进行索引。我知道esi 用于索引,所以我正在尝试使用它...

    scores DWORD MAXIMUMSCORES DUP(0)
optionPromptMsg byte "Type 1 to continue or -1 to exit: ", 0
scorePromptMsg      byte "Enter your numeric score (0-100): ", 0
scoreErrorMsg       byte "Score out of range (0-100)!", 0
optionErrorMsg      byte "Only 0 or 1 allowed in option specification!", 0
resultMsg           byte " scores have been entered.", 0


.code
main proc
mov esi, 0
L1:
    mov edx, offset optionPromptMsg
    call WriteString
    call ReadInt
    mov ebx, 1
    cmp eax, ebx
    je L2
    mov ebx, -1               //99% sure my main is okay
    cmp eax, ebx
    je L3
    mov ebx, -2
    mov ecx, 2
    cmp ebx, eax
    ja L4
    cmp eax, ecx
    ja L4
    L2: call NextScore
    jmp L5
    L4: mov edx, offset optionErrorMsg
        call WriteString
        call Crlf
        jmp L5
    L5:
    loop L1
        L3 : call WriteScore



exit
main ENDP



WriteScore PROC USES esi //Thought was somehow make esi global?

mov eax, lengthof scores  //total number of items added to array
call writeInt
mov edx, offset resultMsg   
call WriteString
mov esi,0
L1:
    mov eax, scores[esi *4]
    call writeInt    //writes the numbers in the array
    inc esi
loop L1
mov eax, 5000
call Delay


ret
WriteScore ENDP

NextScore PROC USES esi

mov edx, offset scorePromptMsg
call WriteString
call ReadInt
mov ebx, 0
mov ecx, 100       
cmp ebx, eax
ja L1
cmp eax,ecx
ja L1
jmp L2
L1:
    mov edx, offset scoreErrorMsg
    call WriteString
    call Crlf
L2:
    mov scores[esi*4], eax //gets the next number and puts it in the array
    inc esi

ret
NextScore ENDP

当我运行它并向数组中添加 3 个项目时,无论出于何种原因,它都说 scores 的长度为 20,然后当它打印出数组时,数字甚至不接近我的数字期待,通常是数百万或只是0

非常感谢任何建议!

【问题讨论】:

  • 添加源的剩余部分。 最低限度,代码需要包含您的ReadIntWriteString 函数。此外,您应该在调试器中运行它 - 如果这是基于 dos 的,请使用 Turbo Debugger 如果您调试代码,您将很快看到您的错误。 ;)
  • @enhnzflep 我正在使用 Visual Studio 编写它,并且它的调试器很好.. 在我看来狗 sh*t。你说的这个涡轮调试器是什么?添加源代码的其余部分是什么意思?
  • @enhzflep :他正在使用 Irvine32 库。一些有用的信息是here
  • 运气减半!有趣的是,我安装 VS 的唯一原因是它的调试器。Turbo Debugger 不会帮助你,因为它适用于在 dos(或 DosBox)下运行的 16 位程序。 @MichaelPetch - 哦,我明白了。 :嗅探:
  • 他的代码是 32 位的,所以如果他设置了库,他应该能够轻松地在 VS 中使用调试器。

标签: arrays assembly indexing x86 masm


【解决方案1】:

您有几个问题。一是您似乎不了解过程/函数上的USES 指令的用途。如果您使用USES 并列出一个寄存器,那么它会告诉汇编器将这些寄存器的值保存在堆栈中,并在函数退出之前恢复它们。这意味着您在该函数中对该寄存器所做的任何更改都不会被调用它的函数看到。

MASM 手册是这样描述用途的:

语法:使用 reglist

说明:

 An optional keyword used with PROC. Generates code to push the
 value of registers that should be preserved (and that will be
 altered by your procedure) on the stack and pop them off when the
 procedure returns.

 The <reglist> parameter is a list of one or more registers. Separate
 multiple registers with spaces.

由于您似乎希望调用函数看到在函数 NextScore 中对 ESI 所做的更改,因此您需要删除 USES 该程序的声明。变化:

NextScore PROC USES esi

到:

NextScore PROC

现在,当您在下一个分数中增加 ESI 时,函数退出时不会撤消。

另一个问题是lengthof 伪操作码:

lengthof:返回数组变量中的项目数。

可能不清楚,但这个伪操作码是汇编代码时数组中的元素数。您可以像这样定义分数数组:

scores DWORD MAXIMUMSCORES DUP(0)

scores 数组的 lengthof 值始终为 MAXIMUMSCORES。而不是使用 lengthof 你应该做的只是使用 ESI 寄存器。您已经使用 ESI 来记录已添加到数组中的元素的计数。所以这段代码:

WriteScore PROC USES esi ; Thought was somehow make esi global?

    mov eax, lengthof scores  ; total number of items added to array
    call WriteInt

可以改为:

WriteScore PROC USES esi ; Thought was somehow make esi global?

    mov eax, esi  ; esi = number of items added to array
    call WriteInt

另一个问题是您似乎不知道loop 指令的工作原理。在 [x86 指令集] 中,loop 指令执行以下操作:

使用 ECX 或 CX 寄存器作为计数器执行循环操作。 每次执行 LOOP 指令时,计数寄存器为 递减,然后检查是否为 0。如果计数为 0,则循环为 终止并且程序继续执行该指令 按照循环指令。如果计数不为零,则接近跳跃 对目的(目标)操作数执行,大概是 循环开始的指令。

在您的代码中,您从未将 ECX 设置为您希望循环的次数,因此它将使用 ECX 中发生的任何值。这就是为什么你打印了很多额外的数字。 ECX 需要初始化。由于您想循环输入所有的分数,您只需将 ESI 移动到 ECX。你的 WriteScore 函数做了:

    mov esi,0
L1:
    mov eax, scores[esi *4]
    call WriteInt    ; writes the numbers in the array
    inc esi
    loop L1

我们可以修改为:

    mov ecx,esi      ; Initialize ECX loop counter to number of scores added
                     ; to the array.
    mov esi,0
L1:
    mov eax, scores[esi *4]
    call WriteInt    ; writes the numbers in the array
    inc esi
    loop L1

现在我们只需遍历用户实际输入的分数 (ESI)。

考虑到这些变化,您的程序可能看起来像这样:

INCLUDE Irvine32.inc
INCLUDELIB Irvine32.lib
INCLUDELIB user32.lib
INCLUDELIB kernel32.lib

MAXIMUMSCORES equ 20

.data
scores DWORD MAXIMUMSCORES DUP(0)
optionPromptMsg byte "Type 1 to continue or -1 to exit: ", 0
scorePromptMsg      byte "Enter your numeric score (0-100): ", 0
scoreErrorMsg       byte "Score out of range (0-100)!", 0
optionErrorMsg      byte "Only 0 or 1 allowed in option specification!", 0
resultMsg           byte " scores have been entered.", 0

.code
main proc
    mov esi, 0
L1:
    mov edx, offset optionPromptMsg
    call WriteString
    call ReadInt
    mov ebx, 1
    cmp eax, ebx
    je L2
    mov ebx, -1               ; 99% sure my main is okay
    cmp eax, ebx
    je L3
    mov ebx, -2
    mov ecx, 2
    cmp ebx, eax
    ja L4
    cmp eax, ecx
    ja L4
L2: call NextScore
    jmp L5
L4: mov edx, offset optionErrorMsg
    call WriteString
    call Crlf
    jmp L5
L5:
    loop L1
L3: call WriteScore

    exit
main ENDP

WriteScore PROC USES esi ; We make changes to ESI not visible to caller
                         ; since we don't intend to change the number of scores
                         ; with this function. Any change to ESI in this function
                         ; will not appear to the caller of this function

    mov eax, esi      ; total number of items added to array in ESI
    call WriteInt
    mov edx, offset resultMsg
    call WriteString
    mov ecx,esi
    mov esi,0
L1:
    mov eax, scores[esi *4]
    call WriteInt    ; writes the numbers in the array
    inc esi
    loop L1
    mov eax, 5000
    call Delay
    ret
WriteScore ENDP

NextScore PROC ; We want changes to ESI to exist after we exit this function. ESI
               ; will effectively act as a global register.
    mov edx, offset scorePromptMsg
    call WriteString
    call ReadInt
    mov ebx, 0
    mov ecx, 100
    cmp ebx, eax
    ja L1
    cmp eax,ecx
    ja L1
    jmp L2
L1:
    mov edx, offset scoreErrorMsg
    call WriteString
    call Crlf
L2:
    mov scores[esi*4], eax ; gets the next number and puts it in the array
    inc esi

    ret
NextScore ENDP

END

此示例输出:

Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 10
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 20
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 40
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 650
Score out of range (0-100)!
Type 1 to continue or -1 to exit: 99
Only 0 or 1 allowed in option specification!
Type 1 to continue or -1 to exit: 1
Enter your numeric score (0-100): 100
Type 1 to continue or -1 to exit: -1
+5 scores have been entered.+10+20+40+650+100

【讨论】:

  • 哦,好吧!不知道为什么,但我的印象是 esi 是循环的计数器!一直忘记那是ecx 的工作。 @Michael_Petch 精彩的解释,真实地展示了一切的原因和方式。
  • @user38254 你可能一直在想movsb, movsw, movsl 类型的指令。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多