MASM 程序集将 8 位寄存器移动到 16 位寄存器(即 mov cx,ch)[重复]
Posted
技术标签:
【中文标题】MASM 程序集将 8 位寄存器移动到 16 位寄存器(即 mov cx,ch)[重复]【英文标题】:MASM Assembly move 8 bit register to the 16 bit register (ie. mov cx, ch) [duplicate] 【发布时间】:2015-04-18 11:38:48 【问题描述】:我决定学习一门汇编编程语言。我正在使用this 8086 tutorial。在底部的练习是找出一些指令中的错误,其中之一是
mov cx, ch
我在这个主题上发现了一些类似的问题,解释了如何实现它,但现在我想知道为什么禁止此操作?
假设我在 CH 中有 10d = 00001010b 并希望将其放入 CL 并同时擦除 CH。 mov cx, ch
似乎这样做是因为它将 10d 显示为 16bit 00000000 00001010 并将其分别放入 CH 和 CL(整个 CX)
它有什么问题,为什么给出的教程要求在这个表达式中查找错误?
【问题讨论】:
它并没有太多被禁止,更多的是“full list of all Intel opcodes(链接到 PDF)中没有提供”。所以是的,这是被禁止的,因为操作码不存在。 我高度怀疑你的很多困惑是作者已经(可能是故意)选择了CH
、CL
和CX
注册这个问题。为了帮助您了解不连续性,您可以得到与mov dx, ch
高度相似的错误,这可能会更好地说明错误的原因。只是一个建议;不满意退款。
相关:MOV 8 bit to 16 bit register (al to bx) 本质上是重复的。
【参考方案1】:
mov
指令用于在相同大小的操作数之间移动。您想要扩展 8 位 ch
到 16 位 cx
。有两条指令可用于此目的:
movzx cx,ch ; zero-extends ch into cx. the upper byte of cx will be filled with zeroes
movsx cx,ch ; sign-extends ch into cx. the upper byte of cx will be filled with the most significant bit of ch
在这种特殊情况下完成同样事情的另一种方法是:
shr cx,8 ; zero-extend
sar cx,8 ; sign-extend
【讨论】:
使用mov cl,ch; xor ch,ch
实现零扩展。无法通过sar cx,8
获得符号扩展,因为移动多个位置需要在8086 上使用cl
。解决方案是mov cl,8; sar cx,cl
,这在8088 和8086 处理器上相当慢。
@chqrlie:这被标记为masm32。这至少意味着一个后 8086 开发环境,并且问题中没有任何内容意味着开发与 8086 向后兼容的代码。这些天几乎无关紧要,除非针对嵌入式 8086 微控制器(或者我猜想使用 emu8086 做作业,但性能并不重要那里)。
@PeterCordes:这个问题实际上被标记为masm
,OP 指的是这个页面:web.archive.org/web/20150318063331/http://www.csi.ucd.ie/staff/… 8086 编程简介...摆弄 8 和 16位寄存器仍然可以在 32 位和 64 位汇编中完成,但最近的教程侧重于或更有趣的东西。
@chqrlie:我说“是”,before I retagged 包括 x86 和零扩展(基于 OP 提到的位模式),因为答案不是特定于 masm32,更不用说马斯。但很公平,我没有查看问题正文中的链接。【参考方案2】:
问题是,您试图将 8 位寄存器 ch
的内容移动到 16 位寄存器 cx
中。你不能这样做,因为寄存器的大小不同。
所以我猜你会收到类似“操作码和操作数的无效组合”的错误消息。
p.s:上面交换了8和16;声明保持不变。检查例如this overview。如您所见,没有定义不同寄存器大小的组合。这意味着不存在任何代表mov cx, ch
的OPcode。
【讨论】:
mov [dest], [src]。我尝试将 8b 移动到 16b。 对不起,我交换了那个。但是这个说法基本上还是有效的:不能在mov
命令中混用 16 位寄存器和 8 位寄存器。【参考方案3】:
您想在 8086 上将 CH
的内容移动到 CX
。
在更新的处理器(例如 80286)上,您只需将 CX
的值向右移动 8 个位置,无论是否使用符号复制:
; zero extend ch into cx
shr cx,8
; sign extend ch into cx
sar cx,8
这些指令在 8088 或 8086 上不可用。您必须使用CL
指定班次计数:
; zero extend ch into cx
mov cl,8
shr cx,cl
; sign extend ch into cx
mov cl,8
sar cx,cl
然而,这种方法非常慢,因为可变数量的位置移位每个位置需要多个周期。
这是一个更快的方法:
; zero extend ch into cx
mov cl,ch
xor ch,ch
; sign extend ch into cx
mov cl,ch
neg ch ; set the carry flag if ch is negative
sbb ch,ch ; set all bits if ch was negative, clear them otherwise
如果您可以销毁 AX,则可以使用专为此设计的 cbw
来节省代码大小。在原始 8086 尤其是 8088 上,小 = 快,因为代码获取是一个主要瓶颈。但是,在现代 x86 上,情况并非如此。
; sign extend ch into ax
mov al, ch
cbw ; sign-extend AL into AX
; optionally move back to cx
xchg cx, ax ; smaller than mov cx, ax
为了避免破坏 AX,你可以这样做 mov cl,ch
; xchg ax,cx
; cbw
并停在那里,或者做一个最终的xchg ax,cx
只是将 CH 符号扩展到 CX 并恢复其他所有内容。 xchg
与 AX 是 1 字节指令,cbw
和 cwd
也是(将 AX 扩展到 DX:AX,例如在 16 位 idiv
之前)
cbw
与 386 movsx ax, al
完全相同。
【讨论】:
在 8086 上更好,其中代码大小是性能的主要因素:mov cl,ch
; xchg ax,cx
; cbw
; xchg ax,cx
。这种事情(AX 之所以特殊,是因为缺少 movsx、2 操作数 imul 等)是 8086 花费 8 个操作码来获得 xchg ax, reg
的单字节短格式的原因。
或者如果你可以销毁AX,mov al,ch
; cbw
; xchg ax,cx
@PeterCordes:确实如此!看起来我们来自同一个游乐场:)
可疑;我从来没有真正运行过我在真正的 8086 上编写的任何东西,甚至根本没有在实模式下运行过。我只是碰巧知道如何优化 x86 的代码大小,为了乐趣和作为性能的决胜局。但是像这样的东西,为了大小而不是速度,主要是为了Tips for golfing in x86/x64 machine code 并了解给当前 x86 带来负担的历史 ISA 设计决策。但是,如果您的意思是我们喜欢为乐趣和利润而优化,那么是的 :)【参考方案4】:
只需简单说明即可
mov cl,ch ; copy high bits to low
xor ch,ch ; clear high-bits
在 16 位编程中很常见,只需要 2 个时钟周期。
movezx/movsx 的使用需要3 个时钟周期。使用
movsx cx,ch
用于使用 sign-extension 和
将字节移动到单词movzx cx,ch
使用 zero-extension
将字节移动到单词【讨论】:
在大多数现代 x86 CPU 上,movzx
/movsx
有 1 个周期延迟,并且是单个 uop。出于吞吐量目的,这意味着它们在典型的 4 宽 OoO exec CPU 上需要 0.25 个周期。但是,从 CH 读取可能会在 Haswell/Skylake 上增加一个额外的延迟周期。在不指定微架构的情况下谈论周期成本是没有意义的。 (在无序 CPU 上,“周期成本”不是问题;性能不是一维成本的简单总和)以上是关于MASM 程序集将 8 位寄存器移动到 16 位寄存器(即 mov cx,ch)[重复]的主要内容,如果未能解决你的问题,请参考以下文章