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

Why am I getting zero from mov ax, bx+si+1?

    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 时得到零?

您的问题是:"Why am I getting zero from 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 个字节分别是 0AFh0B0halax的低字节,所以第一个字节(0AFh)存储在那里,高字节ah得到第二个字节(0B0h),这就是 ax 变成 0B0AFh.

的方式

#2我们说基址寄存器bx和变址寄存器si的存在解释为内存寻址,所以[bx+si+1] 指向内存位置 15 (0AFh)。指令lea代表load effective address,其目的是从数据段内部获取一个地址。您的代码行正在获取内存位置 15 (0AFh) 的有效地址,即 15.

这么多理论需要论证,这里是:

接下来是EMU8086的截图:BLUE箭头指向原始代码行,GREEN箭头指向该行代码被解释(作为内存寻址),RED 箭头显示寄存器 ax (B0AFh).[=43= 中的效果]

现在下一条指令的屏幕截图:BLUE 箭头指向原始代码行,GREEN 箭头指向被解释的代码行(注意它与前一个相同),RED 箭头显示寄存器 ax (0Fh) 中的效果。

最后,我们来测试一下Visual Studio2013的代码:下一张截图证明mov ax, bx+si+1是无效的,而另一行给出了与EMU8086相同的结果(ax=0FH) :

所以,"why are you getting zero from mov ax, bx+si+1?" 因为您的数据段内的内存位置 15 中可能有一个零。你可能认为 bx+si+1 会给你一个正常的数字,15,但现在你知道使用基址寄存器和变址寄存器将被解释为内存寻址,所以你得不到数字15 但内存位置 15.

中的数据

Jose 详细回答了你的代码中发生了什么,你当然应该尝试用你的 emu8086 复制他的结果,这样你就可以单独调试你的代码了。

我只想添加一个 "high-level" 答案。

您可能有点想念什么是汇编程序。它不是常规的编程语言,更像是目标 CPU.

的实际机器代码的别名

这意味着您编写的任何指令几乎总是 1:1 映射到实际的 CPU 指令(一些汇编器支持所谓的 "pseudo instruction",它们被编译成一些真正的指令定义明确的规则 - 这在 x86 上很少见,我不知道 emu8086 中有这样的伪指令。

并且指令 mov register, some_math_expression 不存在,源值必须是单个数字(在操作码或其他寄存器中编码的立即值,或者从计算机内存中获取的值(您的情况))。

令人困惑的部分是,x86 确实支持非常复杂的内存寻址模式,例如在 32b 模式下 [eax+eax*4+imm32] 是有效的(imm32 = 32b 直接在指令中编码的立即值,这就是地址 "count" 被存储,但在 16b 模式下它只是 16b 数字)。在 16b 模式下,addressing are very limited 的值和寄存器的合法组合,但乍一看它们仍然可能看起来像数学表达式。

但他们不是。它在指令操作码中被硬编码,所以每个合法组合都有自己的二进制数,CPU 不会读作 "some bx register plus some di register plus some number",它读取操作码的第二个字节值(二进制它将 10000001 我想,懒得验证),芯片上的晶体管被​​设计为该值作为 [bx+di+displacement] 寻址模式工作。

例如inc byte ptr [bx+di+imm16]mov更简单,只需加载值):

CPU读取指令操作码0xFF = inc,然后读取寻址模式0x81(我想,没验证),所以它知道它是“[bx+di+disp]”,然后它再读取另外两个字节,以获得位移值的 16b,最后将这些值相加得到内存中的 16b 偏移量。

然后它获取段值(此指令默认为 ds,除非段前缀操作码用于覆盖它以用于后续单条指令)并将其添加到偏移量以生成 20b 物理地址内存芯片(请参阅任何实模式寻址文档以了解段+偏移量是如何组合的以及为什么我说的是 20b 而不是 32b)。

然后将这个 20b 值设置在连接到内存芯片的 CPU 的引脚上(通过 "bus" = 很多 "wires" 或 PCB 上的路径,在旧的 8086 上:20寻址,16 个用于数据,还有一些用于 read/write/status 处理),内存芯片被指示从总线上读取的地址加载值(我说的是简单的“8086”计算机,忽略所有重缓存现代 PC 中存在的机制,其中 CPU 不再直接与内存芯片对话)。

一段时间后,内存芯片将设法以这种方式切换其内部状态,总线 "data" 侧的引脚设置为该地址处内存单元中的值,因此 CPU 现在可以 "read" 这些引脚并将其存储到临时未命名寄存器中。然后它对其运行递增过程,并将数据总线引脚设置为新值(总线的地址部分很可能始终保持相同的地址),并指示内存芯片写入该值。

地址 "bx+di+displacement" 的内存中的值就是这样从 6 变成 7 的(例如)。

...我在哪里...

哦,所以它不是自由的数学表达式,而是一种合法的寻址模式,硬连接在 CPU。

不幸的是,emu8086 不会因为缺少 [] 而对你大喊大叫,相反它会默默地产生唯一可能的指令,看起来与你写的相似。

顺便说一句,这应该可以让您更容易理解 LEA 指令以及为什么原始语法使用 [],所以看起来它会访问内存。

lea 是删除了胆量的 mov 指令,所以在内存单元的地址计算到 "mov" 之后它停止了,根本不联系内存芯片,但是用在处理的第一阶段计算的 16b 内存偏移值填充目标寄存器。