X86 c内联汇编中的内核

X86 c内联汇编中的内核,x86,kernel,nasm,bootloader,real-mode,X86,Kernel,Nasm,Bootloader,Real Mode,嗨,我又遇到了一个问题,我试图用GNU汇编语言编写一个内核,但遇到了一些麻烦。 我的内核文件versuch.c如下所示: void kprintf( char hello[]) { /*char* video=(char*)0xb8000; for(int i=0;hello[i]!='\0';i++) { video[i*2]=hello[i]; video[i*2+1]=0x06; }*/ asm("mov %0,%%si"::""(hello))

嗨,我又遇到了一个问题,我试图用GNU汇编语言编写一个内核,但遇到了一些麻烦。 我的内核文件
versuch.c
如下所示:

 void kprintf( char hello[])
   {
   /*char* video=(char*)0xb8000;
 for(int i=0;hello[i]!='\0';i++)
{
    video[i*2]=hello[i];

    video[i*2+1]=0x06;

}*/
    asm("mov %0,%%si"::""(hello));
    //asm("mov 'a',%al;");
    asm("call Schreibe;"    
        "Schreibe:;"
        "lodsb;"
        "cmp $0x00,%al;"
        "je Schreibeende;"
        "mov $0x0e,%ah;"
        "mov $0x00,%bh;"
        "int $0x10;"
        "jmp Schreibe;"
        "Schreibeende:;"
        "ret");

}

void main()
{
    asm("jmp    0x10000");
    char hello[]="hallo";
    kprintf(hello);
    asm(".rept 512;"
        " hlt ;"
        ".endr");
}`
和我的bootloader
bootloader.asm

org 0x7c00
bits    16



    section .text

xor ax,ax
mov ss,ax
xor sp,sp

;xor ax,ax
;mov es,ax 
;mov ds,ax


mov [bootdrive],dl


mov bh,0
mov bp,zeichen

mov ah,13h
mov bl,06h
mov al,1
mov cx,6
mov dh,010h
mov dl,01h

int 10h

load:
mov dl,[bootdrive]
xor ah,ah
int 13h
jc load

load2:
mov ax,0x1000
mov es,ax
xor bx,bx

mov ah,2
mov al,1
mov cx,2
xor dh,dh

mov dl,[bootdrive]
int 13h
jc load2


mov ax,0
mov es,ax

mov bh,0
mov bp,zeichen3

mov ah,13h
mov bl,06h
mov al,1
mov cx,13
mov dh,010h
mov dl,01h

int 10h

mov ax,0x1000
mov es,ax
mov ds,ax
jmp 0x1000:0x000

zeichen db  'hello2'
zeichen3 db 'soweit so gut'
bootdrive db 0
times   510 - ($-$$)    hlt
dw  0xaa55
当我使用命令时:
gcc-c-versuch.c
objcopy-O binary versuch.O versuch.bin
cat bootloader.bin vs uch.bin>myOS.bin
qemu-system-i386 myOS.bin
您可以看到,它通过引导加载程序一直运行到最后打印出“soweit so gut”,但它没有显示versuch.c(内核)文本“hallo”。 也许有人知道我做错了什么

问题是,我之前的内核代码只是稍微更改为上面的代码,至少在“soweit so gut”后面打印了一个字符,这表明内核代码是以某种方式执行和运行的

mov $0xe,%ah
mov $0x0,%bh
int $0x10
他是跑的。 但是当我现在通过
objdump-Mi8086-mi386-bbinary-D myOS.bin查看myOS.bin中的hexcode时,我得到:

 28:    b8 00 10 8e c0          mov    $0xc08e1000,%eax
  2d:   31 db                   xor    %ebx,%ebx
  2f:   b4 02                   mov    $0x2,%ah
  31:   b0 01                   mov    $0x1,%al
  33:   b9 02 00 30 f6          mov    $0xf6300002,%ecx
  38:   8a 16                   mov    (%esi),%dl
  3a:   76 7c                   jbe    0xb8
  3c:   cd 13                   int    $0x13
这是引导加载程序的磁盘读取部分 及

哪个是
jmp 0x10000
部分 及

这是写的部分,所以它一定会跳过写的部分,但是它在myOS.bin文件中。正如我所说,当我的代码与字符串略有不同时,它确实发出了一些东西! 你有什么建议我可以改变什么吗

我最近更改了内核代码versuch.c,而命令和引导加载程序代码保持不变

内核代码(versuch.c):

现在它似乎切换到视频模式,显示一个闪烁的光标,但它不会打印出“Q”

我终于用以下内核代码打印了一封信: versuch.c:

void kprintf()
{
    char* video=(char*)0xb8000;
    for(int i=0;video[i]!=0;i++)
    {
        video[i*2]=0x00;
        video[i*2+1]=0x06;
    }
}
void main()
{
    asm("jmp    0x10000");
    asm("mov    $0x1000,%eax;"
        "mov    %eax,%es;"
        "mov    %eax,%ds");
    asm("mov    $0x0e,%ah;"
        "mov    $0x00,%bh;"
        "mov    'Q',%al;"
        "int $0x10");
    asm(".rept 512;"
        " hlt ;"
        ".endr");
}
void kprintf()
{
    char* video=(char*)0xb8000;
    for(int i=0;video[i]!=0;i++)
    {
        video[i*2]=0x00;
        video[i*2+1]=0x06;
    }
}
void main()
{
    asm("jmp    0x10000");
    asm("mov    $0x1000,%eax;"
        "mov    %eax,%es;"
        "mov    %eax,%ds");
    asm("mov $0x0e,%ah");
    asm("mov $0x00,%bh");
    asm("mov %0,%%al":: "" ('T'));
    asm("int $0x10");
    asm(".rept 512;"
        " hlt ;"
        ".endr");
}
但当我添加另一个函数时,它不再工作,这真的很奇怪

我的新内核代码如下所示:

asm("jmp main");



void main()
{
    char* video=(char*)0xb000;
    for(int i=0;video[i]!=0;i++)
        video[i]=0x07;
    asm("mov    $0x1000,%eax;"
        "mov    %eax,%es;"
        "mov    %eax,%ds");
    char string[]="hall0";
    //kprintf(string);
    for(int i=0;i<5;i++)
    {

    asm("mov $0x0e,%ah");
    asm("mov $0x00,%bh");
    asm("mov %0,%%al":: "" ('a'));
    asm("int $0x10");
    }

    asm(".rept 512;"
        " hlt ;"
        ".endr");
}
kprintf((char *)0x12345);
asm(“jmp main”);
void main()
{
字符*视频=(字符*)0xb000;
对于(int i=0;视频[i]!=0;i++)
视频[i]=0x07;
asm(“mov$0x1000,%eax
mov%eax,%es
“mov%eax,%ds”);
字符字符串[]=“hall0”;
//kprintf(字符串);

对于(inti=0;i您使用什么C编译器

32位甚至64位编译器

(因为您只需使用
gcc
而不是类似于
x86-16bit-gcc
的东西,所以我认为您使用的是32位或64位编译器。)

这行不通-您必须切换到保护模式才能运行32位代码。运行64位代码更为困难

即使使用“仅”内联程序集,编译器本身也会添加一些指令——它们是32位或64位指令

或者你真的使用16位编译器

在这种情况下,您必须非常小心:您使用的是哪种内存模型,这有什么副作用

示例:使用“近”内存模型,行:

video=(char*)0xb8000;
jmp 0x1000:0x000
…将地址0xb8000截断为16位值(0x8000)

使用“远”内存模型,但是大多数编译器通常将0xb8000视为0x000b:0x8000(等于线性地址0x13000)。对于这些编译器,必须使用0xb8000000值访问0xb800:0x0000(线性地址0xb8000)

使用
objcopy
以下行肯定不起作用:

kprintf(hello);
原因是编译器将在内部执行以下操作:

asm("jmp main");



void main()
{
    char* video=(char*)0xb000;
    for(int i=0;video[i]!=0;i++)
        video[i]=0x07;
    asm("mov    $0x1000,%eax;"
        "mov    %eax,%es;"
        "mov    %eax,%ds");
    char string[]="hall0";
    //kprintf(string);
    for(int i=0;i<5;i++)
    {

    asm("mov $0x0e,%ah");
    asm("mov $0x00,%bh");
    asm("mov %0,%%al":: "" ('a'));
    asm("int $0x10");
    }

    asm(".rept 512;"
        " hlt ;"
        ".endr");
}
kprintf((char *)0x12345);
0x12345是存储字符串“hello”的地址。但是,在编译程序时,该地址还不知道

因此,对象文件包含一些信息,说明数字0x12345以后必须被字符串的实际地址替换。链接程序链接程序时,链接器将执行此操作。使用
objcopy
,但是此信息会丢失,数字仍为0x12345

顺便说一下:

您的程序包含以下行:

video=(char*)0xb8000;
jmp 0x1000:0x000

为什么您认为这是
main
的位置?

您使用的是什么C编译器

32位甚至64位编译器

(因为您只需使用
gcc
而不是类似于
x86-16bit-gcc
的东西,所以我认为您使用的是32位或64位编译器。)

这行不通-您必须切换到保护模式才能运行32位代码。运行64位代码更为困难

即使使用“仅”内联程序集,编译器本身也会添加一些指令——它们是32位或64位指令

或者你真的使用16位编译器

在这种情况下,您必须非常小心:您使用的是哪种内存模型,这有什么副作用

示例:使用“近”内存模型,行:

video=(char*)0xb8000;
jmp 0x1000:0x000
…将地址0xb8000截断为16位值(0x8000)

使用“远”内存模型,但是大多数编译器通常将0xb8000视为0x000b:0x8000(等于线性地址0x13000)。对于这些编译器,必须使用0xb8000000值访问0xb800:0x0000(线性地址0xb8000)

使用
objcopy
以下行肯定不起作用:

kprintf(hello);
原因是编译器将在内部执行以下操作:

asm("jmp main");



void main()
{
    char* video=(char*)0xb000;
    for(int i=0;video[i]!=0;i++)
        video[i]=0x07;
    asm("mov    $0x1000,%eax;"
        "mov    %eax,%es;"
        "mov    %eax,%ds");
    char string[]="hall0";
    //kprintf(string);
    for(int i=0;i<5;i++)
    {

    asm("mov $0x0e,%ah");
    asm("mov $0x00,%bh");
    asm("mov %0,%%al":: "" ('a'));
    asm("int $0x10");
    }

    asm(".rept 512;"
        " hlt ;"
        ".endr");
}
kprintf((char *)0x12345);
0x12345是存储字符串“hello”的地址。但是,在编译程序时,该地址还不知道

因此,对象文件包含一些信息,说明数字0x12345以后必须被字符串的实际地址替换。链接程序链接程序时,链接器将执行此操作。使用
objcopy
,但是此信息会丢失,数字仍为0x12345

顺便说一下:

您的程序包含以下行:

video=(char*)0xb8000;
jmp 0x1000:0x000

为什么你认为这是
main
的位置?

它确实知道显示一些wierd字符而不是“hallo”这是一个65位的编译器。你可以尝试反汇编你的代码,看看真正做了什么:
objdump-Mi8086-mi386-bbi