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)中没有提供”。所以是的,这是被禁止的,因为操作码不存在。 我高度怀疑你的很多困惑是作者已经(可能是故意)选择了CHCLCX 注册这个问题。为了帮助您了解不连续性,您可以得到与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 字节指令,cbwcwd 也是(将 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)[重复]的主要内容,如果未能解决你的问题,请参考以下文章

masm 16位汇编 函数原理

masm 16位汇编 函数原理

汇编第二章寄存器

如何在 16 位 MASM 中使用 int 13h 格式化可移动设备(闪存驱动器)?

在MASM中使用32位寄存器而不更改默认段定义大小

汇编语言16位环境搭建(Windows 7 32位)