2016-09-14 78 views
2

嗯,这显然是一个初学者的问题,但这是我第一次尝试制作操作系统在C(其实,我几乎是全新的C ..我习惯了asm)所以,为什么这是无效的?据我所知,在Cpointer只是一个uint16_t用于指向内存中的某个区域,权利(或uint32_t,这就是为什么它不工作)? 我做了以下内核(“我已经做了一个引导程序和所有组件加载生成KERNEL.BIN文件):GCC裸机内联汇编SI寄存器不能很好地与指针

kernel.c

void printf(char *str) 
{ 
__asm__(
"mov si, %0\n" 
"pusha\n" 
"mov ah, 0x0E\n" 
".repeat:\n" 
"lodsb\n" 
"cmp al, 0\n" 
"je .done\n" 
"int 0x10\n" 
"jmp .repeat\n" 
".done:\n" 
"popa\n" 
: 
: "r" (str) 
); 
return; 
} 

int main() 
{ 
char *msg = "Hello, world!"; 
printf(msg); 
__asm__("jmp $"); 
return 0; 
} 

我用下面的命令编译它kernel.c

gcc kernel.c -ffreestanding -m32 -std=c99 -g -O0 -masm=intel -o kernel.bin

返回以下错误:

kernel.c:3: Error: operand type mismatch for 'mov'

为什么可能是这个错误的原因?

+3

会给你一个建议。如果你愿意的话,你可以大便便利,但如果你对这类事情不熟悉,最糟糕的事情就是做内联汇编程序。很容易弄错,你需要适当的输入,输出和clobbers来覆盖所有变化的东西。如果你想用汇编写入,我建议把这些函数写在一个单独的汇编文件中,而不要使用_C_代码。 –

+1

如果您使用内联汇编程序,请谨慎使用它。只有在必要时才使用它,并让_C_代码为您完成工作(包括循环等)。 –

+4

即使是经验丰富的尝试GCC扩展内联汇编程序的_C_开发人员,也可以创建似乎能够工作的代码,然后在_C_优化程序缩减所有内容时,在未来的某个时间点不会采取应有的方式。如果你不知道自己在做什么,那么等待发生的负担就会很大。 –

回答

2

正如Michael Petch已经解释的那样,您只能使用内联汇编来完成C语言无法完成的绝对最小代码。其余部分有内联汇编,但您必须非常小心地设置约束和clobber列表对。

让GCC总是把值传递给正确的寄存器,并指定值应该在哪个寄存器中。

对于你的问题,你可能想要做这样的事情

#include <stdint.h> 

void print(const char *str) 
{ 
    for (; *str; str++) { 
    __asm__ __volatile__("int $0x10" : : "a" ((int16_t)((0x0E << 8) + *str)), "b" ((int16_t)0) :); 
    } 
} 

编辑:您的组件具有您试图传递一个指针在16位寄存器中的问题。这不适用于32位代码,因为32位也是指针大小。 如果您想要生成16位实模式代码,则有-m16选项。但是这并不能使GCC成为真正的16位编译器,它有其局限性。本质上,它在代码中发出.code16gcc指令。

+0

嗯,奇怪的是,似乎没有AL和AH单独的限制。对于具有RH子寄存器(A,C,D或B)的寄存器有一个“Q”限制,但对特定的H寄存器没有限制。很好的解决方法。不过,我可能使用'|'而不是'+'。 –

+0

另外,如果你至少总结了这些注释,并且指出当你用'-m32'编译生成32位代码时,指针实际上是32位的,就像OP正在做的那样,问题的关键部分。 –

+0

@PeterCordes:的确,我觉得有一个约束啊。但是由于那个是特殊的(与从整数大小自动选择的EAX,AX或AL相比),它使我认为稍微复杂一些。这就是我使用C方法的原因。 –

0

不能简单地在32位指针上使用16位汇编指令,并期望它工作。 siesi寄存器(即32位)的低16位。

gcc -m32和-m16都使用32位指针。 -m16只是使用地址大小和操作数大小前缀来完成与普通-m32模式大致相同的操作,但是以实模式运行。

如果您尝试在32位应用程序中使用16位寻址,则会丢弃指针的大部分,并且只需转到不同的地方

试着读一本关于intel 32位寻址模式的书,并且保护模式,你会发现很多事情在这种模式下是不同的。

(如果你尝试切换到64位模式下,你会看到这一切再次发生变化)

引导加载器的东西通常不同,CPU复位迫使16位实模式开始CPU。这与32位保护模式完全不同,这是操作系统所做的第一件事情之一。引导加载程序工作在16位模式,在那里,指针是16位宽(当然,不是,20位宽,当适当的段寄存器附加到地址)

+0

操作数大小和地址大小前缀让16位代码使用32位寄存器,包括32位寻址模式。 'gcc -m16'这样做,仍然使用32位地址和整数。这是超级丑陋,臃肿,但它是技术上有效的16位代码,将在386及更高版本上运行。如果您正确设置了段寄存器,它甚至可以用于某些事情。不过,我同意普遍的看法,认为这似乎不是一个好主意。 –

+0

@PeterCordes,是的,但这是32位地址的一部分。如果你尝试引用'SI'寄存器(而不是'ESI')并强制一个16位地址,汇编器可以放16位前缀。在普通的汇编程序应用程序编程中,您将缺省行为放在程序头文件中(在汇编了一些寻址模式指令后)。如果不这样做,您将获得32位保护模式,32位数据和32位默认地址,如果您一个64位的平台,你会得到32位数据和64位地址作为默认的linux和freebsd(至少)。不了解Windows应用程序环境。 –

+0

单指令操作数大小的前缀在保护模式和实模式之间不会改变。它们只是改变操作数大小:页面保持启用(或禁用),并且段寄存器保持它们的含义(作为GDT/LDT中的索引,或者作为移位4个添加来创建线性地址)。 –