为啥寄存器 ax 中的内存整数总和正确,但寄存器 eax 不正确?

Posted

技术标签:

【中文标题】为啥寄存器 ax 中的内存整数总和正确,但寄存器 eax 不正确?【英文标题】:Why is correct sum of memory integers in register ax but not register eax?为什么寄存器 ax 中的内存整数总和正确,但寄存器 eax 不正确? 【发布时间】:2017-10-11 19:51:26 【问题描述】:

鉴于这个项目——我正在帮助的一个学生的项目——

        global  _start

        section .text
_start:
        mov    ebx, people 
        mov    eax, [ebx + 2]
        add    eax, [ebx + 4]
        add    eax, [ebx + 6]
        add    eax, [ebx + 8]
        mov    ebx, [constant]
        div    bx

section .data

average     dw 0
constant    dw 5
people      dw 0, 10, 25, 55, 125

在将其从 Visual Studio 移植到 Linux 机器以找出问题所在时,我遇到了一些问题:

1) 为什么gdm在发出print $ax的时候显示总和255,而发出print $eax的命令时却出现一个大数字?是不是因为我们添加了 word 值而不是 longword 值?

我确实尝试添加到 ax,而不是 eax,并得到了相同的结果。当我试图将初始值移动到 ax 时,我收到了搬迁投诉。这就是我使用 eax 的原因。

2) 为什么使用div bx 时的商是43,但使用div ebx 时会得到错误的答案?

顺便说一句,我相信我找到了最初的问题,即整数溢出。第 10 行 -- mov ebx, [constant] -- 最初是 mov ebx,constant,这并没有导致将 5 移动到 bx 中。

【问题讨论】:

您正在从具有 16 位偏移量的位置加载 32 位 (eax)。这使您的值重叠。 div 的工作方式不同,请查看说明指南。或搜索堆栈溢出(通常每月大约弹出 2-5 次)。而且您的代码不会终止(只要您只在调试器中跳过它,没关系,只是不要运行它)。 @BoPersson 当我使用 ax 时,同样的事情发生了——添加单词值。我尝试了很多不同的东西。 movzx ecx, word [ebx + 4]; add eax, ecx 等等。 并且mov ebx,constant 将被 NASM 翻译成,在我的偏见看来,mov ebx, "address of label constant" 的正确形式。由于 Intel 语法要求使用方括号 [address expression] 来标记内存访问。不幸的是,有一大群 x86 汇编器(MASM、emu8086、MASM 模式下的 TASM,...)试图变得聪明,并且充当 mov ebx,constant 是“获取变量”。要获得地址,您必须使用关键字offsetmov ebx,offset constant。 NASM 更干净,需要 [] 使用,因此您可以在读取源时轻松查看每个内存访问。 【参考方案1】:

几个问题

    所有数据都定义为word,但代码将其视为dword

    除法之前,您需要通过DXEDX 扩展股息。

    虽然数组中的第一个值为零,但最好将它也包含在代码中。如果数据发生变化,至少代码将保持有效!


解决方案

    保持数据为 16 位并进行相应的编程

    mov    edx, people
    mov    ax, [edx]
    add    ax, [edx + 2]
    add    ax, [edx + 4]
    add    ax, [edx + 6]
    add    ax, [edx + 8]
    xor    dx, dx
    div    word ptr [constant]      ;Divide DX:AX by 5
    ...
    constant    dw 5
    people      dw 0, 10, 25, 55, 125
    

    将数据设为 32 位并进行相应的编程

    mov    edx, people
    mov    eax, [edx]
    add    eax, [edx + 4]
    add    eax, [edx + 8]
    add    eax, [edx + 12]
    add    eax, [edx + 16]
    xor    edx, edx
    div    dword ptr [constant]     ;Divide EDX:EAX by 5
    ...
    constant    dd 5
    people      dd 0, 10, 25, 55, 125
    

看看我是如何避免使用EBX 的?

除法器可以直接从内存中使用它的除法器。 EDX 寄存器(无论如何都会参与除法)也可以同样精细地寻址内存。

减少寄存器破坏是一件好事!

【讨论】:

你是对的。课程的讲师想要不同的数据类型。我的朋友是我正在帮助的一名实习生,他认为这就是为什么鼓励学生在 64 位 Windows Intel 系统上编写程序但添加以单词表示的数字的原因。我添加了 eax 而不是 ax,因为我想看看指令是如何工作的。我不在英特尔汇编程序中编程。我最后使用的汇编语言是 MACRO32 (DEC/VAX)。 解决方案 3:从 16 位加载时零扩展到 32 位。 movzx eax, word [edx]/movzx ecx, word [edx+2]/add eax, ecx/movzx ...。这对于需要更多指令的较小缓存占用空间的大型阵列来说是很好的。 (当然,你会使用 SSE2 来求和。或者更好的是,SSE4 代表pmovzx

以上是关于为啥寄存器 ax 中的内存整数总和正确,但寄存器 eax 不正确?的主要内容,如果未能解决你的问题,请参考以下文章

从内存中的数据设置 x86 部分寄存器(EAX、AX、AH)

带 masm 的寄存器 edx::eax 中的 mul 和内存分配

带 masm 的寄存器 edx::eax 中的 mul 和内存分配

如何用汇编语言读取内存值

CPU寄存器栈指令等03

汇编语言访问寄存器和内存篇---02