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
。这让编译器在替换操作数时使用正确的语法。
我决定尝试在我的基本操作系统中使用 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
。这让编译器在替换操作数时使用正确的语法。