mov ax,10
mov bx,4
mov si,ax
mov ax,bx+si+1
LEA ax,[bx+si+1]
当我一起添加bx,si和1并移动到ax时,结果为0。 在下一行,当我使用LEA时它可以工作,我得到15。
为什么我在使用move时变为零?
答案 0 :(得分:5)
你的问题是:“为什么我从mov ax,bx + si + 1获得零?”。很难给出准确的答案,因为您忘记告诉您使用的是什么编译器,而您的代码段不包含数据段,因此我们无法查看您的数据。我们可以做的是使用数据段中的一些数字测试代码并查看结果:
.model small
.stack 100h
.data
xy db 0A0h,0A1h,0A2h,0A3h,0A4h,0A5h,0A6h,0A7h,0A8h,0A9h,0AAh,0ABh,0ACh,0ADh,0AEh,0AFh,0B0h
.code
mov ax, @data
mov ds, ax
mov ax, 10
mov bx, 4
mov si, ax
mov ax, bx+si+1 ;◄■■ #1 (EXPLANATION BELOW ▼)
LEA ax, [bx+si+1] ;◄■■ #2 (EXPLANATION BELOW ▼)
让我们来说明这里发生的事情:
这是正在发生的事情:
#1 由于存在基址寄存器(bx
)和索引寄存器(si
),因此总和被解释为内存寻址,因此代码获取内存位置15中的数据。ax
寄存器大小为2个字节,因此结果是ax
从内存位置15开始获取2个字节,在我们的数据段中,这2个字节为{{1} }和0AFh
。 0B0h
是al
的低字节,因此第一个字节(ax
)存储在那里,高字节0AFh
得到第二个字节(ah
),这就是0B0h
成为ax
的方式。
#2 我们说基本寄存器0B0AFh
和索引寄存器bx
的存在被解释为内存寻址,因此si
指向记忆位置15([bx+si+1]
)。说明0AFh
代表lea
,其目的是从数据段内获取地址。您的代码行正在获取内存位置15(load effective address
)的有效地址,即15。
这么多理论需要一个演示,现在是:
接下来是EMU8086的屏幕截图: BLUE 箭头指向原始代码行, GREEN 箭头指向解释时的代码行(作为内存寻址), RED 箭头显示注册0AFh
(ax
)中的效果。
现在是下一条指令的屏幕截图: BLUE 箭头指向原始代码行, GREEN 箭头指向代码行,因为它已被解释(注意它与前一个相同), RED 箭头显示注册B0AFh
(ax
)中的效果。
最后,让我们在Visual Studio 2013中测试代码:下一个屏幕截图证明0Fh
无效,另一行提供与EMU8086相同的结果(mov ax, bx+si+1
= ax
):
所以,“你为什么从mov ax,bx + si + 1得到零?”因为数据段内的内存位置可能为零。你可能认为0FH
会给你一个正常的数字,15,但现在你知道使用基数和索引寄存器将被解释为内存寻址,所以你没有得到数字15但是内存位置15内的数据。
答案 1 :(得分:3)
Jose向您详细解答了代码中发生的情况,您当然应该尝试使用emu8086复制他的结果,这样您就可以单独调试代码。
我只想添加一个"高级"答案。
你可能有点想念汇编程序。它不是常规编程语言,但更像是目标CPU的实际机器代码的别名。
这意味着您编写的任何指令几乎总是按1:1映射到实际的CPU指令(某些汇编程序支持所谓的"伪指令",它们通过明确定义的规则编译成几个实际指令 - 这在x86上非常罕见,我在emu8086中并不知道这样的伪指令。
指令mov register, some_math_expression
不存在,源值必须是单个数字(在操作码中编码的立即值,或其他寄存器,或从计算机内存中获取的值(您的情况))。< / p>
令人困惑的部分是,x86确实支持非常复杂的内存寻址模式,在32b模式下,例如[eax+eax*4+imm32]
是有效的(imm32 = 32b立即值直接在指令中编码,那是&#39;地址&#34;计数&#34;存储,但在16b模式下它只有16b数字)。在16b模式下,addressing are very limited的值和寄存器的合法组合,但它们仍然可能看起来像第一眼的数学表达式。
但他们不是。它在指令操作码中是硬编码的,因此每个合法组合都有它自己的二进制数,CPU没有读取它作为&#34;某些bx寄存器加上一些di寄存器加上一些数字&#34;,它读取操作码的第二个字节值(二进制它将是10000001
我认为,懒得验证),并且芯片上的晶体管被设计为该值可用作{{ 1}}寻址模式。
例如,对于[bx+di+displacement]
(inc byte ptr [bx+di+imm16]
更简单,只需加载值):
CPU读取指令操作码mov
= 0xFF
,然后它读取寻址模式inc
(我认为,没有验证),所以它知道它&#39; s&#34; [bx + di + disp]&#34;,然后再读取另外两个字节,得到16b的位移值,最后将这些值加在一起,得到16b偏移到内存中。
然后默认为该指令使用段值(0x81
,除非使用段前缀操作码覆盖它以执行下一条指令)并将其添加到偏移量以将20b物理地址生成到存储器芯片中(参见实模式寻址的任何文档,了解如何组合段+偏移以及为什么我在谈论20b而不是32b)。
然后在连接到存储器芯片的CPU的引脚上设置该20b值(通过&#34;总线&#34; =很多&#34;电线&#34;或PCB上的路径,旧的8086:20用于寻址,16用于数据,还有一些用于读/写/状态处理)和存储器芯片被指示从总线上读取的地址加载值(我在谈论简单&#34; 8086&#34;计算机,忽略了现代PC中存在的所有重型缓存机制,其中CPU不再直接与内存芯片通信。)
一段时间后,内存芯片将设法以这种方式切换它的内部状态,即&#34;数据&#34;总线的一侧设置为该地址的存储单元中的值,因此CPU现在可以&#34;读取&#34;那些引脚并存储到临时未命名的寄存器中。然后它在其上运行递增过程,并将数据总线引脚设置为新值(总线的地址部分很可能在整个时间内保持相同的地址),并指示存储器芯片写入值。
那就是地址处的内存价值&#34; bx + di + displacement&#34;从(例如)转到6到7。
......我在哪里......
哦,所以它不是自由数学表达式,而是合法寻址模式之一,在CPU中硬连接。
不幸的是,emu8086并没有对你失踪的ds
大吼大叫,相反它会默默地产生唯一可能的指令,看起来与你写的相似。
顺便说一句,这应该让您更容易理解[]
指令以及原始语法使用LEA
的原因,因此看起来它会访问内存。
[]
是带有删除内容的lea
指令,因此在将内存单元的地址计算为&#34; mov&#34;它停止,根本不接触存储器芯片,但用目标寄存器填充在处理的第一阶段计算的16b存储器偏移值。