Assembly 汇编新手请求帮助:有符号整数除法+&引用;截断为整数;

Assembly 汇编新手请求帮助:有符号整数除法+&引用;截断为整数;,assembly,x86,masm32,irvine32,Assembly,X86,Masm32,Irvine32,几天前我问了一个关于这个代码的问题,所以你们中的一些人可能会觉得它很熟悉。对于那些不熟悉它的人来说,这段代码应该做的是向用户请求25个带符号的整数,并将它们存储到一个数组中;这是通过RequestSignedits子例程完成的(我很确定这部分工作正常)。然后,一个“calcMean”子例程将数组中的所有值相加,除以数组中的元素数,然后“将结果截断为整数”。这就是我被困的地方。我试图编写一个calcMean子例程来完成我刚才描述的工作,但似乎无法正确地完成除法。最重要的是,我甚至不确定我当前在ca

几天前我问了一个关于这个代码的问题,所以你们中的一些人可能会觉得它很熟悉。对于那些不熟悉它的人来说,这段代码应该做的是向用户请求25个带符号的整数,并将它们存储到一个数组中;这是通过RequestSignedits子例程完成的(我很确定这部分工作正常)。然后,一个“calcMean”子例程将数组中的所有值相加,除以数组中的元素数,然后“将结果截断为整数”。这就是我被困的地方。我试图编写一个calcMean子例程来完成我刚才描述的工作,但似乎无法正确地完成除法。最重要的是,我甚至不确定我当前在calcMean子例程中的内容是否能正常工作。有人能提供帮助吗

INCLUDE    c:\irvine\irvine32.inc
INCLUDELIB c:\irvine\irvine32.lib
INCLUDELIB c:\masm32\lib\user32.lib
INCLUDELIB c:\masm32\lib\kernel32.lib

.data
theSINTArray BYTE 25 dup(?)
lengthOfArray BYTE ?
indexCounter BYTE 0              
prompt BYTE "Please enter a value: ",0


.CODE

main PROC

    call    requestSignedInts
    call    calculateMean
    exit

main ENDP

requestSignedInts PROC

    mov     edx, offset theSINTArray
Next:
    push    edx                                        
    mov     edx,OFFSET prompt                          
    call    WriteString                                
    call    ReadInt
    pop     edx 
    mov     [edx], al
    inc     edx
    cmp     edx, offset theSINTArray + 25
    jb      Next
    ret

requestSignedInts ENDP


calculateMean PROC

push ecx
mov    ecx,lengthOfArray - theSINTArray ; Determine array length    
xor    eax, eax                         ; Clear EAX
mov    esi, OFFSET theSINTArray         ; Starting point for index into array
calcMean:
movsx  edx, byte ptr[esi]               ; Sign extended move a byte into EDX
add    eax, edx                         ; Accumulate in EAX
inc    esi                              ; Increment source pointer to the next element
loop   calcMean                         ; or cmp esi,endOfArray / jb, then you wouldn't need to touch ecx

mov    ecx,lengthOfArray - theSINTArray ; Determine array length    
cdq                                     ; sign-extend eax into edx:eax
idiv   ecx                              ; Divide edx:eax by ecx
                                        ; eax now contains the integer and edx contains
                                        ; the remainder.
pop    ecx
ret

calculateMean ENDP

END     main

在我看来,您可能缺少注释中显示的
idiv
功能。为什么不:

calculateMean PROC

push ecx
mov    ecx,lengthOfArray - theSINTArray ; Determine array length    
xor    eax, eax                         ; Clear EAX
mov    esi, theSINTArray                ; Starting point for index into array
calcMean:
movsx  edx, byte ptr[esi]               ; Sign extended move a byte into EDX
add    eax, edx                         ; Accumulate in EAX
inc    esi                              ; Increment source pointer to the next element
loop   calcMean                         ; or cmp esi,endOfArray / jb, then you wouldn't need to touch ecx

mov    ecx,lengthOfArray - theSINTArray ; Determine array length    
cdq                                     ; sign-extend eax into edx:eax
idiv   ecx                              ; Divide edx:eax by ecx
                                        ; eax now contains the integer and edx contains
                                        ; the remainder.
pop    ecx
ret

calculateMean ENDP

您已经知道列表的长度,只需清除
edx
并除以
ecx
。这也正确地使用
movsx
将字节(从数组)进行符号扩展移动到32位寄存器(
edx
)。总数被累加到eax中,最后,我们进行扩展和除法

与David的答案相比,您的原始代码有更多的奇怪之处。我发布了一个新的答案,而不是进一步编辑大卫的答案

注释中记录了更改。删除旧注释,使更改注释突出

.data
SBYTEArray BYTE 25 dup(?)     ; SINT = signed int, which makes most x86 C programmers think 32bit
; lengthOfArray BYTE ?        ; totally bogus: named wrong, and there doesn't need to be any storage there
; SBYTEArray_len equ $ - SBYTEArray  ; that's NASM syntax and IDK the MASM syntax
SBYTEArray_end:                  ; SBYTEArray_end and prompt have the same address.  That's fine.

prompt BYTE "Please enter a value: ",0      ; If MASM/Windows has a .rodata section, you should put constant data there.  It goes in the text section, along with code, because each instance of your process doesn't need a private copy.


.CODE
main PROC
    call    requestSignedInts
    call    calculateMean
    exit
main ENDP

requestSignedInts PROC
    ; indent code one level deeper than labels / directives
    push    esi                     ; save a call-preserved reg
    mov     esi, offset SBYTEArray

Next:
    mov     edx,OFFSET prompt
    call    WriteString
    call    ReadInt
    mov     [esi], al
    inc     esi
    ; cmp     edx, offset SBYTEArray + 25  ; hard-coding the size defeats the purpose of defining lengthOfArray
    cmp     esi, offset SBYTEArray_end
    jb      Next

    pop     esi          ; note that the push/pop are outside the loop
    ret
requestSignedInts ENDP


calculateMean PROC
    ; push ecx          ; The normal function-call ABIs allow clobbering ecx, and I don't see any reason to make this function go beyond the ABI requirements (although that is an option in asm)
    ; push   esi          ; esi *is* call-preserved in the standard 32bit ABIs.  But by changing our function to use fewer registers, we can avoid the save/restore

    xor    eax, eax                         ; start with sum=0
    mov    ecx, offset SBYTEArray

calcMean:
    movsx  edx, byte ptr[ecx]
    add    eax, edx
    inc    ecx
    cmp    ecx, offset SBYTEArray_end
    jb     calcMean                         ; loop while our pointer is below the end pointer

    mov    ecx, SBYTEArray_end - SBYTEArray ; Determine array length.  Does this need OFFSET?
    cdq
    idiv   ecx
    ; pop esi         ; we ended up not needing it
    ret
calculateMean ENDP

END Main

特别是当您可以通过使用其他内容来保存寄存器时。

除法已经截断为整数,所以什么也不做。顺便说一句,记住edx在除法中的作用。“idiv”指令给出整数结果。正如@harold所说,什么都不做,结果已经在EAX()中被截断了。@harold如果edx有一个“角色”,很遗憾,我不知道它是什么。我假设这个“角色”应该是常识,但作为一个语言新手,我还没有掌握所有的基本知识。edx的作用到底是什么?在链接中搜索“idiv”,edx是除法的剩余部分,EAX是商,你只需要商,对吗?@JoseManuelAbarcaRodríguez我按照你的建议做了,如果我理解正确,我在代码中的一行:“idiv EAX”,将edx:EAX除以EAX。如果这是正确的,我还有两个问题。1.我应该使用不同的寄存器来存储数组中所有元素的总和吗?2.如何将总和的最高有效四个字节放入edx,最低有效四个字节放入eax?如果
eax
可以为负数,则需要使用
cdq
指令将extend
eax
签名到
edx:eax
。使用异或edx、edx/
div
cdq
/
idiv
。绝对正确。这感觉好像OP不明白idiv如何在他的代码中工作。早上我在键盘旁时,我会添加干熄焦。关于OP需要如何在累积到4B寄存器之前使用
movsx
加载字节,还有很多要说的,并且
[edx+indexCounter]
使用indexCounter的地址,而不是它的值。我编辑了干熄焦的
cdq
,但我会把剩下的留给你…谢谢@PeterCordes。我太专注于
idiv
,以至于我真的没有注意到数组索引的愚蠢。我最终给出了一个新的答案,因为OP的代码中仍然有太多不好的地方(例如,ABI违规,比如关闭esi,但保存/恢复ecx)。