【问题标题】:8086- why can't we move an immediate data into segment register?8086-为什么我们不能将立即数数据移入段寄存器?
【发布时间】:2013-10-05 03:53:24
【问题描述】:

在8086汇编编程中,我们只能将数据加载到段寄存器中,首先将其加载到通用寄存器中,然后我们必须将其从该通用寄存器移动到段寄存器中。

为什么不能直接加载呢?不被允许有什么特殊原因吗?

mov ax,5000Hmov ax,[5000H] 有什么区别? [5000h] 是否表示内存位置 5000h 中的内容?

【问题讨论】:

  • 这个问题似乎离题了,因为它是关于一个 30 年历史的处理器的设计理念。
  • @MikeW:这怎么跑题了?如果这个人正在为 8086 编程,为什么不可以在这方面寻求帮助呢?他在问一个实际的问题。如果我问“为什么我不能写入 C 中的任意内存位置?”你会出于同样的原因投票关闭它吗?几乎任何问题都可以概括为“设计决策”。这并不意味着不值得问和知道答案。是的,这里的人都变得如此笨拙地按下关闭按钮。
  • @EdS。 OP 询问为什么不允许某个操作 - 因为工程师是这样设计的。指令集就是这样。争论它是否应该是别的东西不会改变它,也不会帮助编程。
  • @MikeW:是的,你可以用同样的回答“回答”这里大约 90% 的问题。当然,您实际上不会帮助任何人,也不会让网站变得更好。每个设计选择都有其背后的原因(希望如此!),这些原因值得了解。我宁愿让一些有问题的问题溜走(我认为这个问题不属于该类别),而不是拒绝可能有助于其他人的有用问题。
  • @EdS.:在所有模式下都允许写入段寄存器。

标签: assembly x86 x86-16 cpu-registers instruction-set


【解决方案1】:

请记住,汇编语言(任何汇编)的语法只是编写机器代码的一种人类可读的方式。您可以在机器代码中执行的操作的规则取决于处理器电子设备的设计方式,而不是汇编程序语法可以轻松支持的内容。

所以,仅仅因为看起来你可以写 mov DS, 5000h 并且从概念上看似乎没有理由让你不能这样做,它实际上是关于“是否有一种机制可以通过它处理器可以直接从立即数加载段寄存器吗?”

在 8086 组装的情况下,我认为原因很简单,因为工程师没有创建可以将信号从内存 I/O 数据线馈送到写入段寄存器的线的电路.


为什么?我有几个理论,但没有权威的知识。

最可能的原因仅仅是为了简化设计:这样做需要额外的布线和门,而且这是一种不常见的操作(这是 70 年代),不值得在芯片中占用空间。这并不奇怪。 8086 已经过火,允许将任何普通寄存器连接到 ALU(算术逻辑单元),从而允许将任何寄存器用作累加器。我敢肯定,这样做并不便宜。当时的大多数处理器只允许一个寄存器(累加器)用于该目的。


至于括号,您是正确的。假设内存位置 5000h 包含数字 4321h。 mov ax, 5000h 将值 5000h 放入 ax 中,而 mov ax, [5000h] 将 4321h 从内存中加载到 ax 中。本质上,括号的作用类似于 C 中的 * 指针取消引用运算符。

为了强调汇编是机器代码可以做什么的理想化抽象这一事实,您应该注意这两种变体不是具有不同参数的同一条指令,而是完全不同的操作码。他们本可以使用——比如——MOV 作为第一个操作码,MVD(MoVe 直接寻址内存)作为第二个操作码,但他们一定认为括号语法更容易让程序员记住。

【讨论】:

  • 要写入的段寄存器可能用于寻址源操作数”——这听起来不太合理。你可以对现有的指令说同样的话,比如mov bp,[bp]
  • @Ruslan:更重要的是,这个答案是完全错误的。 mov ds, [5000h] 是可编码的:操作码是 mov Sreg, r/m16 立即数是不可编码的 (mov ds, 4321h),因为这需要不同的操作码,但我们确实有一个用于移动到的操作码-Sreg (8E /r) 采用寄存器或内存源。这都是操作码编码空间/解码器复杂性的问题,而不是指令期间使用的段 reg,因为mov ds, [5000h] 就是这种情况。
  • @Ruslan:我想编辑这个答案,这样我就可以将我的赞成票改为反对票,因为我知道这是基于错误的前提。但我认为我不应该在顶部的“这是错误的”横幅中进行编辑,而且我看不到任何其他要编辑的内容。在发布我的答案后,它吸引了另一个赞成票……幸运的是,这是一个 ISA 设计问题,而不是一个直接带有人们会复制的糟糕代码的编程问题,而且 x86-16 大多已经死去并被埋没了,所以很少有人会在这里得到错误的答案会产生负面影响。 OP 仍然有效,也许他们会接受我的回答 :)
  • 顺便说一句,嗨@Euro。抱歉,您的回答中出现了评论噪音。事实证明,当你写这篇文章时,你记错了 x86 是如何工作的,xD。您可能希望根据 mov ds, [5000h] 不可编码的错误前提来编辑很多内容,因为事实上它是。
  • 我知道并非所有你可以用汇编程序编写的东西实际上都是可编码的,并且在没有检查 OP 断言的情况下假设这是这种情况;自从我写汇编程序以来已经有一段时间了。我认为基本解释很好,即使事实证明它不适用于所选择的确切指令。当我有机会时,我将重新调查并编辑/修复答案,希望今晚下班后。如果我发现它无法修复,或者删除它。
【解决方案2】:

x86 机器码只有一个用于移动到 Sreg 的操作码。该操作码是
8E /rmov Sreg, r/m16,并且允许寄存器或内存源(但不是立即的)。

与其他答案中的一些声明相反,mov ds, [5000h] 运行得很好,假设地址 5000h 的 2 个字节对于您所处的模式具有有用的段值。(实模式它们直接用作数字与受保护的地方,其中 Sreg 值是索引 LDT / GDT 的选择器)。

x86 总是对指令的立即形式使用不同的操作码(将常量编码为机器代码的一部分)与寄存器/内存源版本。例如add eax, 123 组装成与 add eax, ecx 不同的操作码。但是add eax, [esi]add eax, ecx 的操作码相同add r, r/m32,只是ModR/M 字节不同。


NASM 列表,来自nasm sreg.asm -l/dev/stdout,以 16 位模式组装一个平面二进制文件并生成一个列表。

我手动编辑将字节分成opcode modrm extra。这些都是单字节操作码(在 ModRM 字节的 /r 字段中没有额外的操作码位借用空间),因此只需查看第一个字节即可了解它是什么操作码,并注意两条指令何时共享相同的操作码。

   address    machine code         source           ;  comments
 1 00000000 BE 0050           mov si, 5000h     ; mov si, imm16
 2 00000003 A1 0050           mov ax, [5000h]   ; special encoding for AX, no modrm
 3 00000006 8B 36 0050        mov si, [5000h]   ; mov r16, r/m16 disp16
 4 0000000A 89 C6             mov si, ax        ; mov r/m16, r16
 5                                  
 6 0000000C 8E 1E 0050        mov ds, [5000h]   ; mov Sreg, r/m16
 7 00000010 8E D8             mov ds, ax        ; mov Sreg, r/m16
 8                                  
 9                            mov ds, 5000h
 9          ******************       error: invalid combination of opcode and operands

支持mov Sreg, imm16 编码需要单独的操作码。这将需要额外的晶体管用于 8086 进行解码,并且会占用更多的操作码编码空间,从而为未来的扩展留下更少的空间。我不确定 8086 ISA 的架构师认为哪些更重要。

请注意,8086 具有特殊的mov AL/AX, moffs 操作码,当从绝对地址加载累加器时可节省 1 个字节。但它不能将mov-immediate 的操作码留给 Sreg?这个设计决策很有意义。您需要多久重新加载一次段寄存器?非常罕见,在真正的大型程序中,它通常不会是常数(我认为)。但是在使用静态数据的代码中,您可能会将累加器加载/存储到循环内的固定地址。 (8086 的代码获取非常弱,所以代码大小 = 大部分时间的速度)。

还请记住,您可以将mov Sreg, r/m16 用于汇编时常量,只需一条额外的指令(如mov ax, 4321h)。但如果我们只有mov Sreg, imm16,运行时变量段值将需要自修改代码。 (所以显然你不会遗漏r/m16 源版本。)我的意思是,如果你只想拥有一个,那肯定是寄存器/内存源版本。

【讨论】:

  • 为了您的说明,将8B F0 设置为mov si, ax 会更有用。不知道如何说服 NASM 发出这个变种。
  • 另外,我不太明白你在最后一段的第一句话中的意思。你是说破坏通用寄存器的那个吗?
  • @Ruslan:同意。 GAS AT&T 语法可以使用mov.s %ax, %si 而不是mov %ax, %si 来选择相反的编码。
  • @Ruslan:我的意思是 mov-immediate。更新。感谢您通读它以指出我不清楚的东西。
  • @Ruslan:我添加了一个db 0x8B, 0xF0 并尝试使用 ndisasm 和 objdump 进行反汇编。甚至没有 objdump 打印mov.s,只是mov 用于两种编码:/ IDK,如果有使用操作数排​​序后缀的 objdump 选项;我没有在手册页中看到一个。 (顺便说一句,.s.intel_syntax noprefix 中工作我并不感到惊讶,我有点过于具体,以便将来的读者清楚,而不是不同的汇编语法专家)。
【解决方案3】:

关于段寄存器

段寄存器与通用寄存器不同(在硬件层面上)。当然,正如 Mike W 在 cmets 中所说,您不能将立即数直接移动到段寄存器的确切原因只有英特尔开发人员知道。但我想,这是因为这样的设计很简单。请注意,此选择不会影响处理器性能,因为段寄存器操作非常少见。所以,多一条指令,少一条指令根本不重要。

关于语法

在所有合理的 x86 汇编语法实现中,mov reg, something 将立即数 something 移动到寄存器 reg。例如:

NamedConst = 1234h
SomeLabel:
    mov  edx, 1234h      ; moves the number 1234h to the register edx
    mov  eax, SomeLabel  ; moves the value (address) of SomeLabel to eax
    mov  ecx, NamedConst ; moves the value (1234h in this case) to ecx

将方括号中的数字关闭表示将具有该地址的内存内容移动到寄存器中:

SomeLabel dd 1234h, 5678h, 9abch

    mov  eax, [SomeLabel+4]  ; moves 5678h to eax
    mov  ebx, dword [100h]   ; moves double word memory content from the 
                             ; address 100h in the data segment (DS) to ebx.

【讨论】:

  • 关于语法的注释暗示MASM语法不合理。我不反对,但重要的是要记住 mov ax, myvar \n... myvar dw 1234 将在 MASM 中将 1234 加载到 ax 中(以及默认模式下的 TASM)。 OTOH、FASM 和 NASM 做得对(更一致),去掉了 offset 关键字。
【解决方案4】:

我记得以前读过原因。我面前没有那个文件,所以请原谅我挥手。

从内存位置或常量加载段寄存器涉及内存周期。如果内存对齐混乱,读取 16 位值可能需要两个内存周期。在周期之间,段寄存器的值是无效的。现在想象一下你在弄乱堆栈段寄存器并且发生了中断:这是你的手推车;尽情享受吧!

【讨论】:

  • 绝对错误,mov ds,[5000H] 是可编码的,但 mov ds, 5000H 不是。如果有一个mov Sreg, imm16 操作码(没有),它不能执行,直到它的所有字节都被提取到解码缓冲区中。因此,您提出问题的唯一说明是 可编码的形式之一。
猜你喜欢
  • 2021-11-26
  • 2011-10-03
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
  • 2019-12-14
  • 2018-08-29
  • 1970-01-01
相关资源
最近更新 更多