首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MOV r/m8,r8与MOV r8,r/m8的差异

MOV r/m8,r8与MOV r8,r/m8的差异
EN

Stack Overflow用户
提问于 2017-06-02 18:23:41
回答 3查看 1.6K关注 0票数 6

通过查看英特尔的指令量,我发现:

1) 88 /r MOV r/m8,r8

2) 8A /r MOV r8,r/m8

当我在NASM中写这样的一行时,用列表选项组装它:

mov al, bl

我在清单上看到了这个:

88D8 mov al, bl

很明显,NASM选择了上述两种指令中的第一种,但第二种指令不是第二种吗?如果是这样,那么NASM选择第一个的依据是什么?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-06-02 19:27:33

这两种编码的存在是因为modr/m字节只能编码一个内存操作数。因此,为了同时允许mov r8,m8mov m8,r8,需要两个编码。当然,这样我们可以对mov的使用进行编码,两个操作数都是使用编码的寄存器,而nasm只是随机选择一个操作数。没有特殊的选择理由,我也见过汇编程序做出了不同的选择。

我还听说过一个汇编程序,它通过选择一种特定的指令编码方式来组装带有水印的二进制代码。这样,“汇编程序”的作者就可以追踪并起诉使用他的汇编程序的人,而不用付钱。

票数 5
EN

Stack Overflow用户

发布于 2017-06-03 00:05:43

有不同编码的有趣的副作用:来自A86手册。

  1. A86利用可以为同一指令生成多个操作码集的情况。(例如,MOV、BX可以使用89或8B操作码生成,方法是反转以下有效地址字节中的字段。这两种形式在功能和执行速度上是完全相同的。)在这种情况下,A86采用了一种不同寻常的选择组合。这将创建一个代码生成“占用空间”,它不会占用程序文件中的空间,但将使我能够判断并在法庭上演示A86是否生成了一个重要的对象文件。这种“足迹”的规范非常模糊和复杂,因此不可能偶然地重复。我主张对我所选择的特定“足迹”拥有专属权利,并禁止任何人复制它。这至少有两个具体的影响: a.任何复制“足迹”的汇编程序都是我的。如果没有确定它是我的,并根据这些条款签发,那么那些出售或分发组装者将受到起诉。 任何标有“足迹”的程序都是由我的汇编程序制作的。它受上述条件5的限制。
票数 8
EN

Stack Overflow用户

发布于 2017-06-02 21:22:58

正如哈罗德所指出的:没有理由。

也许作者发现向前移动的使用比反向移动更有吸引力,或者他们只是拿起了第一个操作码。

我看了一下NASM源代码,发现编码基本上是用一个大的查找表来完成的,所以这确实是一个品味问题。使用其他操作码(8AC3)可以简化代码(我猜),如果解析没有使用查找表:像addps这样的指令是不对称的,通过使用8A /r for mov al, bl,代码可以被重用来计算addps的ModR/M字节和类似的指令。addps xmm0, xmm3在使用mov al, bl时使用与mov al, bl相同的ModR/M字节(C3)。

注意,寄存器A (B)和xmm0 (xmm0)是用相同的数字编码的。

然而,找出为什么会有两种编码仍然很有趣。

作为Mark (重新)发现,早期的x86指令编码在八进制中更有意义。

八进制中的字节有三个数字,我称之为G (Group,oPeration,Flags)。

G是八进制组,同一组中的指令往往执行类似的任务(例如算术和移动)。

然而,这并不是一个严格的划分。

P是运算;例如,在算术组中,一个运算是减法,另一个运算是加法。

F是用来控制操作行为的位集。每个组和操作都按照自己的意愿使用数字F,它甚至可能不是一个位设置(例如,G=2,P=7是mov r16, imm16,F用于选择r16)。

对于从内存/寄存器移动到寄存器或相反方向的mov指令,G是2,P是1。

F是一个具有语义的3位字段:

代码语言:javascript
复制
  2   1   0    bit
+---+---+---+
| s | d | b |
+---+---+---+

s = 1 if moving to/from a segment register
    0 if moving to/from a gp register

d = 1 if moving mem -> reg
    0 if moving mem <- reg

b = 1 if moving a WORD
    0 if moving a BYTE

我们可以开始形成操作码,但是我们仍然错过了选择操作数的方法。

代码语言:javascript
复制
G=2, P=1, F={s=0, d=0, b=0} 210 (88) mov r/m8, r8
G=2, P=1, F={s=0, d=0, b=1} 211 (89) mov r/m16, r16
G=2, P=1, F={s=0, d=1, b=0} 212 (8A) mov r8, r/m8
G=2, P=1, F={s=0, d=1, b=1} 213 (8B) mov r16, r/m16
G=2, P=1, F={s=1, d=0, b=0} 214 (8C) mov r/m16, Sreg
G=2, P=1, F={s=1, d=0, b=1} 215 (8D) Not a move, segment registers are 16-bit
G=2, P=1, F={s=1, d=1, b=0} 216 (8E) mov Sreg, r/m16
G=2, P=1, F={s=1, d=1, b=1} 217 (8F) Not a move, segment registers are 16-bit

操作码后必须输入ModR/M字节,用于选择寻址方式和寄存器。

以八进制为单位,ModR/M字节可视为三个字段:X、M。

X和M结合在一起形成寻址模式。

R选择寄存器(例如0= A,3= B)。

其中一种寻址模式(X=3,M=any)允许我们寻址寄存器(通过M),而不是内存。

例如,X=3、R=0、M=3 (C3)将寄存器B设置为“内存”操作数,而寄存器A设置为寄存器操作数。

而X=3、R=3、M=0 (D8)则将寄存器A设置为“内存”操作数,而寄存器B设置为寄存器操作数。

在这里,我们可以看到歧义所在: ModR/M字节允许我们对源寄存器和目标寄存器进行编码。同时,操作码允许我们编码从源到目的地或从目的地到源的移动--这使我们可以自由选择哪个寄存器是什么。

例如,假设我们要将B移动到A中。

如果我们以A作为寄存器操作数(源),B作为内存操作数(目标),那么ModR/M字节是X=3,R=0,M=3 (C3)。

要从B移动到A,在您的示例中,仅使用较低的8位,我们将移动编码为G=2、P=1、F={s=0、d=1、b=0} (8A),因为我们移动mem->reg (B->A)。因此,最终指令是8AC3。

如果选择A作为内存操作数(目的),B作为寄存器操作数(源),则ModR/M字节为X=3、R=3、M=0 (D8)。

移动是G=2,P=1,F={s=0,d=0,b=0} (88),因为我们移动reg->mem (B->A)。

最后的指令是88D8。

如果要移动整个16位寄存器(此处忽略操作数大小前缀),只需设置F的b位:

G=2,P=1,F={s=0,d=1,b=1}为第一个病例,导致8BC3。

G=2,P=1,F={s=0,d=0,b=1}为第二个病例,导致89D8。

你可以用ndisasm来检查这个

代码语言:javascript
复制
00000000  8AC3              mov al,bl
00000002  88D8              mov al,bl
00000004  8BC3              mov ax,bx
00000006  89D8              mov ax,bx
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44335265

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档