GCC内联汇编英特尔语法“;错误:寄存器“的使用无效”;
我决定尝试在我的基本操作系统中使用c语言中的内联汇编(使用intel语法,因为我已经习惯了NASM)。我遇到了一些我无法解决的问题,我试着到处寻找,但没有任何效果。尝试错误消息后显示的代码时,GCC显示以下消息:GCC内联汇编英特尔语法“;错误:寄存器“的使用无效”;,c,gcc,x86,inline-assembly,intel-syntax,C,Gcc,X86,Inline Assembly,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("mo
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\u syntax noprefix
,然后在asm字符串末尾切换回.att\u syntax
,这样编译器的其余代码仍然可以工作。()
接下来,您有许多asm()
块。最好有一个,这样就可以为整个块指定输入/输出/clobbers/goto。这很容易做到,因为C连接相邻的字符串
您的代码中有NASM样式的[es:di]
。在GAS的Intel语法方言中,段名称在括号外:es:[di]
。我还注意到,[di]
并不合适,除非您使用的是16位体系结构;方括号中的东西必须是平台上指针的大小
…然而,您也在使用int0x10
,这是一种BIOS中断,因此我猜16位实际上是您在这里想要的
尽管如此,您确实需要养成指定重击者的习惯。也就是说,指定代码修改的寄存器,以及它是否修改条件代码寄存器和内存。编译器正在决定将哪些变量保存在寄存器等中,而不知道汇编代码的作用。您必须告诉它您正在修改,例如,ax
,以便它知道并可以保存/恢复它
GCC并不真正了解分段(它是一个采用平面内存模型的便携式编译器,不专门用于x86-16),因此将es
与ds
保留不同的基并不安全。e、 g.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”
。看到了吗
volatile
在这里不是绝对必要的,因为没有输出操作数,asm语句是隐式的volatile
。但最好包含volatile
,以明确(对人类读者)存在明显的副作用(BIOS调用和存储到视频内存)。因此,如果您确实包含一个输出操作数,它将不会得到优化
附录:
通常,使用
-masm=intel
进行编译,而不是使用清单中的.intel\u语法
。这使得编译器在替换操作数时可以使用正确的语法。。首先,您使用的是Intel语法,这很好(至少对于GCC来说,对于clang来说更难)。您可以在编译时使用-masm=intel
,也可以在asm模板中作为黑客开关切换到.intel\u syntax noprefix
,然后在asm字符串末尾切换回.att\u syntax
,这样编译器的其余代码仍然可以工作。()
接下来,您有许多asm()
块。最好有一个,这样就可以为整个块指定输入/输出/clobbers/goto。这很容易做到,因为C连接相邻的字符串
您的代码中有NASM样式的[es:di]
。在GAS的Intel语法方言中,段名称在括号外:es:[di]
。我还注意到,[di]
并不合适,除非您使用的是16位体系结构;方括号中的东西必须是平台上指针的大小
…然而,您也在使用int0x10
,这是一种BIOS中断,因此我猜16位实际上是您在这里想要的
尽管如此,您确实需要养成指定重击者的习惯。也就是说,指定代码修改的寄存器,以及它是否修改条件代码寄存器和内存。编译器正在决定将哪些变量保存在寄存器等中,而不知道汇编代码的作用。您必须告诉它您正在修改,例如,ax
,以便它知道并可以保存/恢复它
GCC并不真正了解分段(它是一个采用平面内存模型的便携式编译器,不专门用于x86-16),因此将es
与ds
保留不同的基并不安全。e、 g.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”
。看到了吗
volatile
在这里不是绝对必要的,因为没有输出操作数,asm语句是隐式的volatile
。但最好包含volatile
,以明确(对人类读者)存在明显的副作用(BIOS调用和存储到视频内存)。因此,如果您确实包含一个输出操作数,它将不会得到优化
附录:
通常,使用-masm=intel
进行编译,而不是使用清单中的.intel\u语法
。这使得编译器在替换操作数时可以使用正确的语法。GCC是否使用操作数