c - 将变量移动到 cl 并使用内联汇编执行 shr

标签 c assembly x86 intel inline-assembly

所以我试图将以下赋值从 C 语言转换为内联汇编

resp = (0x1F)&(letter >> (3 - numB));

假设变量声明如下

unsigned char resp;
unsigned char letter;
int numB;

所以我尝试了以下方法:

_asm {
        mov ebx, 01fh
        movzx edx, letter
        mov cl,3
        sub cl, numB // Line 5
        shr edx, cl
        and ebx, edx
        mov resp, ebx
}

或以下内容

_asm {
            mov ebx, 01fh
            movzx edx, letter
            mov ecx,3
            sub ecx, numB
            mov cl, ecx // Line 5
            shr edx, cl
            and ebx, edx
            mov resp, ebx
    }

在这两种情况下,我都会在第 5 行遇到大小操作数错误。 我怎样才能实现正确的转变?

最佳答案

E*X寄存器是 32 位,而 *L寄存器是 8 位。同样,在 Windows 上,int类型是 32 位宽,而 char类型是 8 位宽。您不能在一条指令中任意混合这些尺寸。

因此,在您的第一段代码中:

sub cl, numB // Line 5

这是错误的,因为 cl寄存器存储一个 8 位值,而 numB变量的类型是 int ,它存储一个 32 位值。您不能从 8 位值中减去 32 位值; SUB 的两个操作数指令大小必须相同。

同样,在您的第二段代码中:

mov cl, ecx // Line 5

您正试图将 ECX 中的 32 位值移动到 8 位 CL 寄存器中。没有某种截断就不会发生这种情况,因此您必须明确指出。 MOV指令要求它的两个操作数具有相同的大小。

( MOVZXMOVSX 是此规则的明显异常(exception),操作数类型必须与单个指令匹配。这些指令分别对较小的值进行零扩展或符号扩展,以便将其存储到一个更大的寄存器。)

但是,在这种情况下,您甚至需要 MOV 指令。请记住,CL 只是完整的 32 位 ECX 寄存器的低 8 位。因此,设置 ECX 也会隐式设置 CL。如果你只需要低 8 位,你可以在后续指令中使用 CL。因此,您的代码变为:

  mov   ebx, 01fh              ; move constant into 32-bit EBX
  movzx edx, BYTE PTR letter   ; zero-extended move of 8-bit variable into 32-bit EDX
  mov   ecx, 3                 ; move constant into ECX
  sub   ecx, DWORD PTR numB    ; subtract 32-bit variable from ECX
  shr   edx, cl                ; shift EDX right by the lower 8 bits of ECX
  and   ebx, edx               ; bitwise AND of EDX and EBX, leaving result in EBX
  mov   BYTE PTR resp, bl      ; move lower 8 bits of EBX into 8-bit variable

对于上面讨论的相同操作数大小匹配问题,我还必须更改最后的 MOV操作说明。您不能将存储在 32 位寄存器中的值直接移动到 8 位变量中。您将不得不移动低 8 位或高 8 位,从而允许您使用 BLBH寄存器,它们是 8 位,因此匹配 resp 的大小.在上面的代码中,我假设您只需要低 8 位,所以我使用了 BL .

另请注意,我使用了 BYTE PTRDWORD PTR规范。这些在 MASM(或 Visual Studio 的内联汇编程序)中并不是绝对必要的,因为它可以从变量的类型中推断出类型的大小。但是,我认为它增加了可读性,并且通常是推荐的做法。 DWORD表示 32 位;它与 int 大小相同和一个 32 位寄存器 ( E*X )。 WORD表示 16 位;它与 short 大小相同和一个 16 位寄存器 ( *X )。 BYTE表示 8 位;它与 char 大小相同和一个 8 位寄存器(*L*H)。

关于c - 将变量移动到 cl 并使用内联汇编执行 shr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40752763/

相关文章:

assembly - 为什么 IA-32 有一个非直观的调用者和被调用者寄存器保存约定?

c - 切换字母(无字符串或字符串函数)

c - 带有时区偏移量的 c 中的 strptime

c - 在c中检查数组是否为空的宏

有人可以帮我解释一下银行选择寄存器的概念吗?

c - 如何将参数传递给函数,从 Assembly 到 C

java - 使用编译器从 Java 到机器代码

c - 当我尝试从字符串结构中获取数据时应用程序卡住?

assembly - 获取EAX寄存器的上半部分

[var] 和 var 之间的汇编差异