好吧,这显然是一个初学者的问题,但这是我第一次尝试在C
中制作操作系统(实际上,我对C
几乎是全新的…我习惯了asm
)所以,为什么这是无效的?据我所知,C
中的pointer
只是用于指向内存中某个区域的uint16_t
,对吧(或uint32_t
,这就是为什么它不起作用)?我已经制作了以下内核("我已经制作了一个引导加载程序,并且都是汇编的,用于加载生成的kernel . bin文件):
kernel.c
void printf(char *str)
{
__asm__(
"mov si, %0n"
"pushan"
"mov ah, 0x0En"
".repeat:n"
"lodsbn"
"cmp al, 0n"
"je .donen"
"int 0x10n"
"jmp .repeatn"
".done:n"
"popan"
:
: "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'
究竟为什么可能是这个错误的原因?
正如Michael Petch已经解释的那样,您只能使用内联汇编来完成在c中无法完成的绝对最小的代码。对于其余的代码,可以使用内联汇编,但您必须非常小心地设置约束和正确的clober列表。
让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指令。
您不能简单地在32位指针上使用16位汇编指令并期望它能工作。si
是esi
寄存器(32位)的下16位。
gcc -m32和-m16都使用32位指针。-m16只是使用address-size和operand-size前缀来做与普通-m32模式基本相同的事情,但在实模式下运行。
如果你试图在32位应用程序中使用16位寻址,你将丢掉指针的高位部分,并简单地转到不同的位置。
试着读一本关于intel 32位寻址模式和保护模式的书,你会发现在这个模式下很多事情都是不同的。
(如果你尝试切换到64位模式,你会看到一切又变了)
一个引导加载程序是不同的,通常情况下,cpu复位强制cpu开始在16位实模式。这与32位保护模式完全不同,后者是操作系统首先要做的事情之一。引导加载程序在16位模式下工作,在那里,指针是16位宽(好吧,不是,20位宽,当适当的段寄存器追加到地址时)