8086 中通用寄存器之间的差异:[bx] 有效,[cx] 无效?

Differences between general purpose registers in 8086: [bx] works, [cx] doesn't?

在8086中这个结构是正确的:

mov bh,[bx]

但这不正确:

mov bh,[cx]

我不知道为什么。我认为通用寄存器(AX、BX、CX、DX、SP、BP、SI 和 DI)是我们可以用于任何目的的寄存器,BX 用于基地址或 CX 用于计数器的声明只是一个约定,它们根本没有区别。但似乎我错了。你能解释一下原因吗?这些寄存器之间的确切区别是什么? (比如为什么不能在cx寄存器中保存基地址?)

在 8086(和 x86 中的 16 位寻址)上,只有以下寻址模式可用:

[bx]       [bx + foo]
[foo]      [bp + foo]
[si]       [si + foo]
[di]       [di + foo]
[bx + si]  [bx + si + foo]
[bx + di]  [bx + di + foo]
[bp + si]  [bp + si + foo]
[bp + di]  [bp + di + foo]

其中 foo 是某个常数值,例如123 或段内符号的偏移量,例如文字 foo 在某处引用 foo: 标签。 (有趣的事实:编码 [bp] 的唯一方法实际上是 [bp+0],汇编器会为你做这个。注意 table [foo][bp]否则会是;这反映了 x86 机器代码如何将编码表示为没有寄存器的位移的特殊情况。)

bp 作为基础意味着 SS(堆栈)段;其他寻址模式暗示 DS(数据)段。如有必要,可以用前缀覆盖。


请注意,不存在涉及 cx 的寻址模式,因此 [cx] 不是有效的内存操作数。

寄存器 ax、cx、dx、bx、sp、bp、si 和 di 被称为通用寄存器,因为它们在所有通用指令中都可以作为操作数访问.这与特殊用途的寄存器如 es、cs、ss、ds(段寄存器)、ip(指令指针)或标志寄存器形成对比,这些寄存器只能通过为此目的制定的特殊指令访问。

如您所见,并非所有通用寄存器都可以用作内存操作数的变址寄存器。注册代码时必须牢记这一点。

除此限制外,还有一些指令隐式操作固定寄存器。例如,loop 指令只对 cx 进行操作,而 16 位 imul r/m16 只对 dx:ax 进行操作。如果您想有效地使用这些指令,请牢记每个通用寄存器的建议用途。

值得注意的是,lods / stos / scas / movs / cmps use DS:SI or/and ES:DI implicitly, and on cx when used with a rep or repz / repnz prefix,因此那些用于在数组上循环指针的寄存器允许代码大小优化。

General purpose 是指这些寄存器可以作为"general purpose instructions"的操作数,例如movadd.

然而,所有这些寄存器至少具有一项特殊功能(列表不完整):

  • ax 始终为 mul / div 操作提供输入并接收结果
  • ax 因为默认的累加器寄存器有一些更短的各种指令编码
  • bx是四个寄存器(bxbpdisi)之一,可用于间接内存寻址在 16 位寻址模式下。
  • cx 被多个指令用作计数器,例如移位计数、looprep
  • dx 包含 16 位到 32 位乘法结果的高位,以及 32 位到 16 位除法中输入的高位
  • sppushpop指令以及各种callret类型控制传输指令的影响和使用。也被硬件中断异步使用。
  • bpenterleave 指令的影响。 (但不要使用enter,它很慢)。
  • sidimovsb
  • 等字符串指令使用

在8086上,只有以下寻址方式可用。总共有17个。一般来说,写同一个地址的方法不止一种。例如 [a][b][c] 可能是 [a + b + c].

的有效表示

segment:[a]表示地址[a]是相对于一个段地址segment。 (有关详细信息,请参阅下文 link。)

# Displacement
[foo]

# Register, Indirect
[bx] = ds:[bx]
[bp] = ss:[bp]
[si] = ds:[si]
[di] = ds:[di]

# Indexed Addressing
foo[bx] = [bx + foo] = ds:[bx + foo]
foo[bp] = [bp + foo] = ss:[bp + foo]
foo[si] = [si + foo] = ds:[si + foo]
foo[di] = [di + foo] = ds:[di + foo]
# where ds:[] indicates the base address, given by the 16
# bit base offset register `ds` (or `ss`)
# The 8086 uses a 20 bit addressing mode of which the high
# 16 bits are set by the segment offset and the low 16 bits
# are set by the bx, bp, si and di registers. The calculated
# address is non-unique, as 12 of the 16 bits from each register
# overlap. See the Intel programmers manual for more details

# Based Indexed Addressing
[bx + si] = ds:[bx + si]
[bx + di] = ds:[bx + di]
[bp + si] = ss:[bp + si]
[bp + di] = ss:[bp + si]
# the data segment is used for addressing modes intended for use with
# data (the first two in this list)
# the stack segment is used for addressing modes intended for use with
# the stack (the last two in this list)

# Displacement + Based Indexed
foo[bx + si] = ds:[bx + si + foo]
foo[bx + di] = ds:[bx + di + foo]
foo[bp + si] = ss:[bp + si + foo]
foo[bp + di] = ss:[bp + di + foo]
# These are the same as above with an additional offset `foo`

(参见:8086 Addressing Modes

foo 是一些任意值。请注意,不存在涉及 cx 的寻址模式,因此 [cx] 不是有效的内存操作数。