程序集8086(IA32)在添加两个数组的元素方面遇到了麻烦

时间:2018-05-03 15:48:32

标签: arrays assembly x86

所以我学习了一些基本的程序集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 个答案:

答案 0 :(得分:1)

你确实很好地观察了这种行为,而你(大多数情况下?)是正确的。

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

  

我在修改实际注册存储内容方面遇到了一些麻烦。

eax这样的通用寄存器是32位寄存器(在支持64位的现代x86 CPU上,eaxrax的低32位部分,这是64位寄存器)。这意味着寄存器包含32位值(0或1)。没有其他的。除非你将它转换为不同的解释,否则调试器通常会将值显示为32位无符号十六进制整数,因为从十六进制1234ABCD这样的输出可以读取头中的特定位模式(每个十六进制数字正好是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

这可能是有意义的,如果你确实计划使用索引值,那么你需要简单的i,并且你打算稍后使用数组地址,所以不修改它们是方便的等等...... / p>

还有其他方法可以访问内存中的值,但上述内容对于学习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]