【问题标题】:Cannot move 8 bit address to 16 bit register无法将 8 位地址移动到 16 位寄存器
【发布时间】:2015-11-27 14:25:11
【问题描述】:

我正在尝试分配变量来注册这里是代码:

       ORG 100h

        var1 DB 10  ;
        var2 DB 20  ;

        MOV BX,var1 ; error : operands do not match: 16 bit register and 8 bit address
RET
END

但如果将第 4 行换成:

MOV BL, var1;

它有效。所以我的问题是为什么不能我将8位变量移动到更大的16位寄存器

我已经提到了thisthisthis OP,但它没有回答我的问题。

注意:

  1. 我使用的是emu8086汇编器
  2. 我是汇编语言的新手,如果这是一个愚蠢的问题,我深表歉意。

【问题讨论】:

  • 您需要使用movzxmovsx 分别以零或符号扩展名移动。它可能在 8086 中不可用,在这种情况下,您需要像您所做的那样移动到 BL 并自己将 BH 归零。至于为什么:因为他们以这种方式创建架构:)
  • @Jester:不是,只是 cbw。我更新了stackoverflow.com/tags/8086/info 标签wiki,其中包含一个指向维基百科的链接,其中包含一个何时引入各种指令的表格。还有一个关于在 16 位 8086 上学习 asm 是多么愚蠢的谩骂。但无论如何,缺少一些主要的 insns 8086 是 movzx/sx 和 bt* bit-manip。
  • 投票只是因为该错误消息措辞不当且具有误导性。
  • 顺便说一句,这实际上 与您链接的 stackoverflow.com/questions/15540450/… 的副本。那里的答案没有试图解释为什么它是一个错误,并且 movzx 在 8086 上不起作用,但仍然如此。投票关闭它作为这个的副本,因为这个有更详细的答案。

标签: assembly x86 x86-16 emu8086


【解决方案1】:

为什么我不能将 8 位变量移动到更大的 16 位寄存器中?

因为机器代码MOV 指令需要源操作数和目标操作数的大小相同。这是必需的,因为 MOV 指令本身并没有指定如何填充较大目标寄存器的剩余位。

为了允许不同大小的移动操作,英特尔在 80386 CPU 中添加了MOVZXMOVSX,这允许更小的源操作数(目标始终是 32 位寄存器)。 -SX 和 -ZX 后缀表示目标寄存器以前未使用的位应该填充什么。

在 16 位 Intel 处理器上,有指令 CBW(将字节转换为字),该指令将符号从 8 位扩展到 16 位。不幸的是,这仅适用于累加器(寄存器 AL/AX),因此您必须执行以下操作:

mov al,var1
cbw
mov bx,ax

cbw 进行符号扩展。如果您的var1 未签名,您可以这样做:

mov bl,var1
xor bh,bh  ; equivalent to mov bh,0 but faster and only one byte opcode

或者,正如 Peter Cordes 所说:

xor bx,bx    ;clear the whole destination register
mov bl,var1  ;update the least significant byte of the register with the 8-bit value

【讨论】:

  • xor bx,bx / mov bl, var1 会更好,因为它更像是您希望在 32 位或 64 位模式下使用的好习惯之一(在使用较小部分之前将整个寄存器归零)。它打破了对完整寄存器旧内容的错误依赖,但分别将上半部分和下半部分归零不会。此外,movs/zx r16, r/m8 是有效的。它也适用于 64 位 dest reg,但不鼓励 movzx 这样做,因为它与具有 32 位 dest 的 movzx 相同。 movsx r64, r/m32 有一个新的操作码,但 movzx 没有。
  • 另外,mov bh,bh 是一个 2 字节指令,代码大小与 mov bh, 0 相同。如果您要编写 BH,mov 实际上更适合现代 CPU 上的 8 位寄存器。 How exactly do partial registers on Haswell/Skylake perform? Writing AL seems to have a false dependency on RAX, and AH is inconsistent。 (但是这两种方法都不好,而不是对整个 16 位寄存器进行异或归零,就像我之前的评论一样。)
【解决方案2】:

您正在使用一个汇编程序来跟踪您如何声明符号以确定要使用的操作数大小。

var1 不是 8 位地址,它是指向两个 8 位变量中的第一个的 16 位地址(不计算段)。因此,汇编程序错误消息措辞不当且令人困惑。

NASM 会按照你说的做,并进行 16 位加载。你会在bl 中找到var1,在bh 中找到var2。大概你可以写mov bx, word [var1],或word ptr或其他什么,让你的汇编器发出16位负载。

(实际上,NASM 会将mov BX, var1 组装成mov r16, imm16,将地址放入寄存器。为了一致性,始终在内存引用周围使用[],因为这适用于英特尔语法的NASM 和MASM 变体。NASM不过,不支持 mov BX, offset var1 语法来编写 mov-immediate 形式。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-26
    • 2017-07-12
    • 2014-05-25
    相关资源
    最近更新 更多