GCC inline assembly intel syntax "Error: invalid use of register"

GCC inline assembly intel syntax "Error: invalid use of register"

我决定尝试在我的基本操作系统中使用 c 中的内联汇编(使用 intel 语法,因为我已经习惯了 NASM)。我遇到了一些我无法解决的问题,我试着到处找,但没有任何效果。在尝试错误消息后显示的代码时,GCC 显示这些消息:

main.c: Assembler messages:
main.c:8: Error: invalid use of register

代码为:

int main(){
    asm("mov ah, 0x00\n\t");
    asm("mov al, 0x13\n\t");
    asm("int 0x10\n\t");
    asm("mov bx, 0xA000\n\t");
    asm("mov es, bx\n\t");
    asm("mov di, 0\n\t");
    asm("mov byte [es:di], 0x0f\n\t"); // Problem line of error.
    while(1){
        asm("nop");
    }
    return 0;
}

有人可以帮助我吗?

关于这个有很多值得一提的地方。首先,您使用的是 Intel 语法,这很好(至少对于 GCC,使用 clang 更难)。您可以在编译它时使用 -masm=intel,或者在您的 asm 模板中切换到 .intel_syntax noprefix,然后在您的 asm 字符串末尾切换回 .att_syntax,这样其余的编译器的代码仍然有效。 (How to set gcc to use intel syntax permanently?)

接下来,您有很多 asm() 块。有 one 可能更好,因此您可以为整个块指定输入/输出/clobbers/goto。这可以很容易地完成,因为 C 连接相邻的字符串。

您的代码中包含 NASM 风格的 [es:di]。在 GAS 的 Intel 语法方言中,段名称位于括号之外:es:[di]。我还注意到,除非您使用的是 16 位体系结构,否则 [di] 并不合适;方括号中的东西必须是平台上指针的大小...

...但是 您还使用了 int 0x10,这是一个 BIOS 中断,所以我猜 16 位实际上是您在这里想要的。

综上所述,您确实需要养成指定 clobbers 的习惯。即指定你的代码修改了哪些寄存器,是否修改了条件码寄存器和内存。编译器正在决定将哪些变量保存在寄存器等中,并且不知道您的汇编代码做了什么。你必须告诉它你正在修改,例如 ax,所以它知道并且可以 save/restore 它。

GCC 并不真正了解分段(它是一个假定平面内存模型的可移植编译器,而不是专门针对 x86-16),因此将 es 与不同的基数分开并不安全ds。例如GCC 可能会选择使用 rep stosd 来初始化结构或数组。这里很安全,因为 asm 之后的代码是最少的。

这是我重写的代码。

int main()
{
    asm volatile(
        ".intel_syntax noprefix\n\t"
        "mov ah, 0x00\n\t"
        "mov al, 0x13\n\t"
        "int 0x10\n\t"
        "mov bx, 0xA000\n\t"
        "mov es, bx\n\t"
        "mov di, 0x00\n\t"
        "mov byte ptr es:[di], 0x0f\n\t"
        ".att_syntax"                      // undo .intel_syntax
        : /* output operands */
        : /* input operands */
        : "ax","bx","di","cc", "memory" /* clobbers */
    );
    while(1){
        // asm("nop");  // unneeded: empty infinite loops are already legal in C.
    }
    return 0;
}

如果您的代码修改甚至 读取 您也通过 C 指针访问的内存,您还可以在 clobbers 行中包含 "memory"。请参阅 documentation for inline assembly for gcc, and

volatile 在这里不是绝对必要的,因为没有输出操作数,asm 语句隐式为 volatile。但最好包含 volatile 以明确(对人类读者)存在可见的副作用(BIOS 调用和存储到视频内存)。因此,如果您确实包含了一个输出操作数,它就不会被优化掉。

附录: 一般情况下,使用-masm=intel编译,而不是在列表中使用.intel_syntax。这让编译器在替换操作数时使用正确的语法。