6

我正在尝试使用我的打印功能在屏幕上打印一些东西。

我偶然发现了一个小问题 - 当我像这样传递字符数组时:

char s[] = "abc";
print(s);

它工作正常,但是当我这样称呼它时,没有任何效果。

print("abc");

这是我的函数声明

//print function 
void print(char* message);

我错过了什么吗?printf 的工作方式相同,您可以通过第二种方式传递字符串。

编辑:

定义

void print_at(char* message, int col, int row){
    if(col >= 0 && row >= 0){
        set_cursor(get_screen_offset(col,row));
    }
    int i = 0;
    while(message[i] != 0){
        print_char(message[i++],-1,-1,WHITE_ON_BLACK);
    }
}
void print(char* message){
    print_at(message, -1,-1);
}

EDIT2:kernel.o 的 objdump

void start(){
    clear_screen();
    char s[] = "abc";
    print("abc");
    print(s);
    while(1);
}

部分.text的反汇编:

00000000 <_start>:
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   83 ec 28                sub    esp,0x28
   6:   e8 00 00 00 00          call   b <_start+0xb> //clear_screen()

   b:   c7 45 f4 61 62 63 00    mov    DWORD PTR [ebp-0xc],0x636261 //"bca"
  12:   c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
  19:   e8 00 00 00 00          call   1e <_start+0x1e> //print()

  1e:   8d 45 f4                lea    eax,[ebp-0xc]
  21:   89 04 24                mov    DWORD PTR [esp],eax
  24:   e8 00 00 00 00          call   29 <_start+0x29> //print()

  29:   eb fe                   jmp    29 <_start+0x29>
  2b:   90                      nop

编辑3:

由于这可能与我初始化环境的方式有关,因此这里有两个负责的文件:

pmode.asm - 初始化段,并跳转到内核的开始

[bits 16]
switch_to_pm:

    cli     ; switch interuppts off
    lgdt [gdt_descriptor] ; load global descriptor table 

    mov eax, cr0 ; set control registers first bit to protected mode
    or eax, 0x1
    mov cr0, eax 

    jmp CODE_SEG:init_pm ;flush cache by far jump

[bits 32]
init_pm:
    mov ax, DATA_SEG
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov ebp, 0x90000
    mov esp, ebp

    call BEGIN_PM

这是我构建gdt的方式:

; GDT

gdt_start: 

gdt_null: ; the mandatory null descriptor 
    dd 0x0      ; ' dd ' means define double word ( i.e. 4 bytes ) 
    dd 0x0 

gdt_code: ; the code segment descriptor 
    ; base =0 x0 , limit =0 xfffff , 
    ; 1 st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 
    ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 
    ; 2 nd flags : ( granularity )1 (32- bit default )1 (64- bit seg )0 ( AVL )0 -> 1100 b 
    dw 0xffff       ; Limit ( bits 0-15) 
    dw 0x0          ; Base ( bits 0-15) 
    db 0x0          ; Base ( bits 16-23) 
    db 10011010b    ; 1 st flags , type flags 
    db 11001111b    ; 2 nd flags , Limit ( bits 16-19) 
    db 0x0          ; Base ( bits 24-31) 
gdt_data: ; the data segment descriptor 
    ; Same as code segment except for the type flags : 
    ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 
    dw 0xffff       ; Limit ( bits 0-15) 
    dw 0x0          ; Base ( bits 0-15) 
    db 0x0          ; Base ( bits 16-23) 
    db 10010010b    ; 1 st flags , type flags 
    db 11001111b    ; 2 nd flags , Limit ( bits 16-19) 
    db 0x0          ; Base ( bits 24-31) 

gdt_end:    ; The reason for putting a label at the end of the 
            ; GDT is so we can have the assembler calculate 
            ; the size of the GDT for the GDT decriptor ( below ) 
            ; GDT descriptior 
gdt_descriptor:
    dw gdt_end - gdt_start - 1  ; Size of our GDT , always less one 
                                ; of the true size 
    dd gdt_start                ; Start address of our GDT 

    ; Define some handy constants for the GDT segment descriptor offsets , which 
    ; are what segment registers must contain when in protected mode. For example , 
    ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 
    ; case is the DATA segment (0 x0 -> NULL ; 0 x08 -> CODE ; 0 x10 -> DATA ) 
    CODE_SEG equ gdt_code - gdt_start 
    DATA_SEG equ gdt_data - gdt_start
4

2 回答 2

1

在查看了一个更大字符串的反汇编之后,我找到了答案。

原因是我链接内核的方式。这些是我被建议使用的命令:

ld -o kernel.bin -Ttext 0x1000 $^ --oformat binary

但由于我有一个 windows gcc,并且我的 asm 和 C 文件是 elf 格式,我不得不使用这个技巧:

ld -o kernel.out -Ttext 0x1000 $^ 
objcopy -O binary -j .text kernel.out $@

这仅复制了对象的文本部分,所以我得到了二进制版本。由于我只复制了对象的.text部分,因此保存在.rdata部分中的字符串丢失了。因此,只需将其添加到 objcopy 即可:

objcopy -O binary -j .text -j .rdata kernel.out $@
于 2013-08-12T17:15:13.393 回答
-1

当您说 print(s) 时,您将其作为 char 数组传递,因为您已声明“s”。但是当你作为 print("abc") 传递时。“abc”的类型是什么。它没有定义。我想这是你的问题。我还建议您将 char s[] 更改为 char *s。希望这可以帮助。

于 2013-08-12T02:37:05.643 回答