为啥寄存器 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
是“获取变量”。要获得地址,您必须使用关键字offset
:mov ebx,offset constant
。 NASM 更干净,需要 []
使用,因此您可以在读取源时轻松查看每个内存访问。
【参考方案1】:
几个问题
所有数据都定义为word,但代码将其视为dword。
除法之前,您需要通过DX
或EDX
扩展股息。
虽然数组中的第一个值为零,但最好将它也包含在代码中。如果数据发生变化,至少代码将保持有效!
解决方案
保持数据为 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 和内存分配