代码之家  ›  专栏  ›  技术社区  ›  NGSBNC

组件8086(IA32)在添加两个阵列的元素时出现问题

  •  0
  • NGSBNC  · 技术社区  · 6 年前

    因此,我正在学习一些基本的汇编IA-32,在理解如何实际注册存储内容时遇到了一些困难。下面的程序应该按索引位置(a1[i]+a2[i])将两个给定数组的元素相加,并将结果存储在第二个寄存器中。数组的长度相同。我使用gdb,所以我知道循环部分工作正常。

    EXIT = 1
    WRITE = 4
    LINUX_SYSCALL =0x80
    
    .data
    array1: .int -1, 5, 1, 1, 4  # um vetor de inteiros
    array2: .int 1, -3, 1, -5, 4 # vetor que fica com a soma dos dois
    
    .text
    
    .global _start
    _start: 
                 movl   $array1,            %eax
                 movl   $array2,            %ebx
    
        ifThen:
    
                 jz     fim
                 jmp    soma    
    
        soma:
                 add    %eax,               %ebx
                 jmp    next_pos
    
        next_pos:
                 inc    %ecx
                 add        $4,                 %eax
                 add        $4,                 %ebx
                 jmp    ifThen
    
        fim:
                 movl   $EXIT,              %eax
                 int    $LINUX_SYSCALL
    

    我最初的想法是,如果ebx寄存器中的值被正确添加(因此没有写系统调用),那么gbd就太透明了。相反,我一直在注册表中看到大量数字,我假设这些数字是地址,而不是数组中元素总和的结果。但是,如果我删除movl指令(movl array1,%eax)中的美元符号,我会得到预期的数字,但无法转到数组的下一个位置,因为add指令实际上会向寄存器中的值添加4,而不是将寄存器指针移到下一个4字节。

    感谢您的帮助,提前感谢!

    1 回复  |  直到 6 年前
        1
  •  1
  •   Ped7g    6 年前

    你确实很好地观察到了这种行为,而且(大部分情况下)是这样的纠正他们的错误。

    movl $array1, %eax vs公司 movl array1, %eax :是,将加载第一个 eax 使用内存地址,将加载第二个 eax公司 具有来自内存(来自该地址)的32位值。

    我很难理解如何真正注册商店的东西。

    通用寄存器如下 eax公司 是32位寄存器(在支持64位 eax公司 是的低32位部分 rax ,即64位寄存器)。这意味着寄存器包含32位值(0或1)。没有别的了。除非您切换到不同的解释,否则调试器通常会将值显示为32位无符号十六进制整数,因为从输出来看就像十六进制一样 1234ABCD 您可以读取head中的特定位模式(每个十六进制数字正好是4位,即。 B =11=8+2+1=1011二进制),但这并不意味着寄存器包含十六进制值,寄存器只有32位,您可以按照自己(或代码)的意愿对其进行解释。

    使用索引访问数组元素 i 您可以从不同的技术中进行选择,在对数组求和的任务中,我可能会使用直接在元素上使用内存地址的原始代码,但随后您需要再使用一个寄存器来加载实际值,即:

        # loop initialization
        movl $array1, %eax   # eax = array1 pointer
        movl $array2, %ebx   # ebx = array2 pointer
        # TODO: set up also some counter or end address
    loop_body:
        # array1[i] += array2[i];
        movl (%ebx), %edx    # load value array2[i] from memory into edx
        addl %edx, (%eax)    # add edx to the array1[i] (value in memory at address eax)
        # advance array1 and array2 pointers (like ++i;)
        addl $4, %eax
        addl $4, %ebx
        # TODO: do some loop termination condition and loop
    

    这允许使用简单的正文循环代码,并使用不同的数组提供相同的求和代码进行求和。

    其他选项

    通过将寄存器直接编码到内存访问指令中,可以避免使用内存地址注册,如:

        # loop initialization
        xorl %ecx, %ecx      # ecx = 0 (index + counter)
    loop_body:
        # array1[i] += array2[i];
        movl array2(,%ecx,4), %eax  # load value array2[i] from memory into eax
        addl %eax, array1(,%ecx,4)  # add eax to the array1[i]
        incl %ecx                   # ++i
        # TODO: do some loop termination condition and loop
    

    但此代码无法重定向到不同的阵列。

    也可以在寄存器中使用数组地址,但通过使用索引寄存器寻址避免对其进行修改:

        # loop initialization
        movl $array1, %eax   # eax = array1 pointer
        movl $array2, %ebx   # ebx = array2 pointer
        xorl %ecx, %ecx      # ecx = 0 (index + counter)
    loop_body:
        # array1[i] += array2[i];
        movl (%ebx,%ecx,4), %edx  # load value array2[i] from memory into edx
        addl %edx, (%eax,%ecx,4)  # add edx to the array1[i]
        incl %ecx                 # ++i
        # TODO: do some loop termination condition and loop
    

    如果您确实计划使用索引值,那么这可能是有意义的,所以您需要 ,并且您也计划稍后使用数组地址,因此不修改它们很方便,等等。。。

    还有其他方法可以访问内存中的值,但对于学习x86汇编的人来说,以上方法最简单。

    请记住,在汇编中没有变量或数组等。。计算机内存就像一个没有名字的巨大数组,索引从0到N-1(N=物理内存的大小),每个索引上都有一个字节(8位信息)。

    寄存器类似于CPU芯片上直接可用的8/16/32/64位信息,因此CPU不需要知道地址(名称“eax”类似于地址),也不需要联系内存芯片获取值(因此寄存器比内存快)。

    联系AT&中的存储器;T语法必须以以下形式编写: displacement(base_reg, index_reg, scale) ,请详细查看此问题: A couple of questions about [base + index*scale + disp]