为什么我从mov ax,bx + si + 1得到零?

时间:2016-11-21 11:27:03

标签: assembly x86-16 emu8086

    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时变为零?

2 个答案:

答案 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 ▼)

让我们来说明这里发生的事情:

enter image description here

这是正在发生的事情:

#1 由于存在基址寄存器(bx)和索引寄存器(si),因此总和被解释为内存寻址,因此代码获取内存位置15中的数据。ax寄存器大小为2个字节,因此结果是ax从内存位置15开始获取2个字节,在我们的数据段中,这2个字节为{{1} }和0AFh0B0hal的低字节,因此第一个字节(ax)存储在那里,高字节0AFh得到第二个字节(ah),这就是0B0h成为ax的方式。

#2 我们说基本寄存器0B0AFh和索引寄存器bx的存在被解释为内存寻址,因此si指向记忆位置15([bx+si+1])。说明0AFh代表lea,其目的是从数据段内获取地址。您的代码行正在获取内存位置15(load effective address)的有效地址,即15。

这么多理论需要一个演示,现在是:

接下来是EMU8086的屏幕截图: BLUE 箭头指向原始代码行, GREEN 箭头指向解释时的代码行(作为内存寻址), RED 箭头显示注册0AFhax)中的效果。

enter image description here

现在是下一条指令的屏幕截图: BLUE 箭头指向原始代码行, GREEN 箭头指向代码行,因为它已被解释(注意它与前一个相同), RED 箭头显示注册B0AFhax)中的效果。

enter image description here

最后,让我们在Visual Studio 2013中测试代码:下一个屏幕截图证明0Fh无效,另一行提供与EMU8086相同的结果(mov ax, bx+si+1 = ax):

enter image description here

所以,“你为什么从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存储器偏移值。