2013-04-20 2 views
2

Я пытаюсь создать простое ядро ​​с использованием сборки и C on linux с помощью bochs. Проблема, что каждый раз, когда я пытаюсь вызвать код c из кода сборки, эмулятор дает мне ошибку и сбрасывает симуляцию. когда я начинаю в 16 битном режиме я использую этот кодKernel Dev In assembly & C

global _start 

[bits 16] 

_start: 

mov [BOOT_DRIVE] , dl 

mov bp , 0x7000 
mov sp , bp 

mov bx , 0x0000 ;load 5 sectors to 0x0000(ES):0x9000(BX) 
mov es , bx 
mov bx , KERNEL_OFFSET 
mov dh , 15 
mov dl , [BOOT_DRIVE] 
call disk_load 

mov dx , [es:KERNEL_OFFSET] 
call print_hex 

call switch_to_pm 

jmp $ 

и после переключения в защищенный режим это код

[bits 32] 
extern kmain 

begin_pm: 

;print a char to vram for testing and it is printed, this means the switch has suceeded 
mov ebx , VRAM_ADDRESS 
mov al , 'H' 
mov [ebx] , al 
mov al , 0x07 
inc ebx 
mov [ebx] , al 

call kmain 

jmp $ 

и функция с в основном ничего не делает просто пустой один

о командах, я использую я использую эти

nasm $asm_file_name.asm -f elf -o $asm_file_name.o 
gcc -ffreestanding -c $c_file_name.c -o $c_file_name.o 
ld -o $c_file_name.bin -Ttext 0x7c00 $asm_file_name.o $c_file_name.o --oformat binary 
dd status=noxfer conv=notrunc if=$c_file_name.bin of=$floppy_name.img 

не знаю я, где это Prob Лем. Есть идеи ?

Примечание: Я использую следующие GDT и switch_to_pm функции

;GDT 
gdt_start: 

    gdt_null: 
     dd 0x0 
     dd 0x0 

    gdt_code: ;the code segment descriptor 
     ; base = 0x0 , limit = 0xfffff , 
     ; 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 ; 1st flags , type flags 
     db 11001111b ; 2nd 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 
     dd gdt_start 

    ; Size of our GDT , always less one 
    ; of the true size 
    ; 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 
[bits 16] 

switch_to_pm: 

cli 
lgdt[gdt_descriptor] 

mov eax , cr0 
or eax , 0x1 
mov cr0 , eax 

jmp CODE_SEG:init_pm 

[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 , 0x9000 
mov esp , ebp 

jmp begin_pm 
+1

Где находится ваш 'switch_to_pm'? Вы пытались разобрать результирующий двоичный файл и посмотреть, есть ли в нем какие-то неправильные адреса? Я уверен, что есть. –

+0

После разборки кода я обнаружил, что вызов функции c имеет правильный адрес. –

+0

Ну, вы пытались добавить 'jmp $' сразу после 'begin_pm'? особенно 'call kmain' может выйти из строя, если вы забыли установить ss или ds. –

ответ

1

call kmain попытается найти функцию по адресу, как если бы весь код был загружен в 0x7c00. Однако весь код загружается с диска на 0x9000. Таким образом, вам нужно попросить компоновщика исправить адрес, который он использует, в качестве адреса для kmain.

А еще лучше, поскольку @AlexeyFrunze предлагает загрузить код, начиная со второго сектора, в место в памяти непосредственно после того, где первый сектор загружен BIOS.

+1

Если ОП просто загружает оставшиеся сектора в 0x7e00 (= 0x7c00 + 0x200), он должен работать, если не будет другой ошибки. –

+0

Я тоже исправил это. и теперь он отлично работает. большое спасибо –