操作系统与网络实现 之七

来源:互联网 发布:hive sql 编辑:程序博客网 时间:2024/05/29 16:04

 

使用C语言编写内核

到目前为止,我们可以使用32位编程了,但是用汇编编程还是一件比较枯燥、比较痛苦的事,下一步我们想用C语言编写32位系统,那么怎么办?

办法就是在kernel.asm使用call语句直接调用C程序。

具体过程如下:

汇编文件kernel.asm生成中间文件kernel.asmo

C文件kernel.c生成中间文件kernel.o

这两个中间文件再链接生成kernel.bin文件,具体过程参见makefile

有一点要注意,这里我们使用的djgpp在编译c语言时会在函数名加上下划线,那么在asm中要调用这个函数,也必须在函数名下加下划线,才能让链接程序找到这个函数,才可以正确编译。

 

kernel.asm源码:

[BITS32]

[GLOBAL start]      ;导出 start这个入口,以便让链接器识别 ,

[EXTERN _ya_main]  ;用到本文件外定义的函数kernel.c

jmp        start

start:

call _ya_main       ;调用C

jmp$

 

 

kernel.c源码:

void ya_main()

{

 unsignedint* addr=(int*)0x10050;

 unsignedshort*video_addr;

 unsignedint mid=*addr;

 video_addr =(int*)mid;

 //计算点的偏移量

 unsignedint offset=50*(800+1)+250;

 video_addr = video_addr+ offset ;

 for(int i=0; i<50; i++){

 *( video_addr)=0x7ff;//蓝色

 video_addr ++;

 }

}

 

kernelloader.asm源码:

 

[BITS16]  

jmp                main

gdt_entries   equ   3            ;共有三个段描述符:null,os code32,os data32

pe              equ   1            ;bit PE in CR0

null           equ   0h

os_code32_selequ   8h           ;1,gdt,rpl=00

os_data32_selequ   10h          ;2,gdt,rpl=00

VESA:       times256db0       ;分配一块区域存放 vesa返回的信息,大小256,我们只需要其中的一个32位值

pdescr      times6db0

gdt_table   times(gdt_entries*8)db0

 

set_video_mode:                   ;设置显卡模式

 push             es

 

 ;设置显卡模式

 mov              ax,0x4f02

 mov              bx,0x4114                            ;800X600 ( 5:6:5 ) 16位色彩

 int              0x10

 ;取得该模式下显卡线性地址

 mov              bx,0x1000

 mov              es,bx

 mov              di, VESA ;es:di指向256空间,int 10h将在此填写数据

 mov              ax,0x4f01

 mov              cx,0x114

 int              0x10

 ;40个字节开始存有显卡地址0xe0000000,将此地址再存入指定的地址0x10050

 mov              eax,[es:VESA+40]

 ;将此地址再存入指定的地址0x10050,

 mov              [es:0x50],eax                   ;eax内容为 0xe0000000

 

 pop              es

 ret

read_kernel:                                              ;读入 kernel程序

 push             es

 

 .rk:

 mov              ax,0x8000                           ;kernel.bin所在的段基址           

 mov              es,ax

 mov              bx,0                                 ;写入到内存0x8000:0000物理地址=0x80000

 mov              ah,2

  mov              dh,0                                 ;磁头

 mov              dl,0                                 ;驱动器号

 mov              ch,0                                 ;磁道0

 mov              cl,4                                 ;4个扇区开始

 mov              al,1                                 ;读入扇区数,每个扇区为 512B

 int              0x13   

 jc               .rk

 

 pop              es

 ret

 

main:

movax,1000h

movds,ax

;设置显卡模式

call             set_video_mode

;读入 kernel

call             read_kernel

;打开 A 20 地址线

mov              ax,0x2401

int              0x15 

;[1]built up GDT table

cli

mov eax,gdt_table

;item 0:null descriptor,

mov dword[eax],0

mov dword[eax+4],0

add eax,8

;item 1,OS code32 descriptor,

;Base=00000000h,limit=0ffh,G=1,D=1,type=a,dpl=0

mov word[eax],0ffh

mov word[eax+2],0

mov byte[eax+4],00h

mov byte[eax+5],09ah

mov byte[eax+6],0c0h

mov byte[eax+7],00h

add eax,8

;item 2,OS data32 descriptor

;Base=00000000h,Limit=0fffffh,G=1,D=1,Type=2,DPL=0

mov word[eax],0ffffh

mov word[eax+2],0000h

mov byte[eax+4],00h

mov byte[eax+5],092h

mov byte[eax+6],0cfh   ;高四位是G D 0 AVL,此处为1100 = c ,低四位是limit bit 16-19此处为f

mov byte[eax+7],00h

add eax,8

;[2]built false GDT descriptor

mov word[pdescr+0],(gdt_entries*8)

mov dword[pdescr+2],gdt_table+00010000h

lgdt[pdescr]

;[3]enter into protected mode

;刷新CR0

moveax,cr0

or eax,pe

movcr0,eax

jmp flush

flush:

movax,os_data32_sel

movds,ax

moves,ax

movss,ax

movfs,ax

movgs,ax

jmpdword os_code32_sel:0x80000 ;跳转到0x8000:0000保护模式 物理地址0x80000

 

 

boot.asm

[BITS16]                                                 ;编译成16位的指令

[ORG0x7C00]

jmp                main

 

read_kernelloader:                                       ;读入 kernelloader程序

 push             es

 

 .rk:

 mov              ax,0x1000                           ;kernelloader.bin所在的段基址           

 mov              es,ax

 mov              bx,0

 mov              ah,2

 mov              dl,0

 mov              ch,0

 mov              cl,2

 mov              al,2                                ;读入扇区数,每个扇区为 512B

 int              0x13 

 jc               .rk

 

 pop              es

 ret

 

main:                                                      ;主程序         

 mov              ax,0x0                              ;boot.bin程序的段基址

 mov              ds,ax

 

 call             read_kernelloader                    ;读入 kernelloader 程序 

 

 jmpdword        0x1000:0                             ;跳转到 kernelloader处执行

times510-($-$$)db0

db0x55

db0xAA

 

 

 

makefile

######################

#声明要编译的所有组成,这里的ya是本工程名称,可以取任何名字,这里就用ya

######################

ya:out/boot.bin out/kernelloader.bin out/kernel.asmo out/kernel.o out/kernel.ld  out/kernel.bin out/creat_img.exe out/write_in_img.exe A B C D

#开始对各部分编译,注意不是空格是Tab

out/boot.bin:code/boot.asm

   nasm code/boot.asm -o out/boot.bin

out/kernelloader.bin:code/kernelloader.asm

   nasm code/kernelloader.asm -o out/kernelloader.bin

#编译asm文件,生成中间文件

out/kernel.asmo:code/kernel.asm

   nasm -f aout code/kernel.asm -o out/kernel.asmo

#编译C文件,生成中间文件

out/kernel.o:code/kernel.c

   gcc -fpack-struct -std=c99 -c code/kernel.c -o out/kernel.o

#链接内核

out/kernel.ld:out/kernel.asmo out/kernel.o

   ld  -Ttext 0x80000 -e start -o out/kernel.ld out/kernel.asmo out/kernel.o

#生成可执行代码文件

out/kernel.bin:out/kernel.ld

   objcopy -R .note -R .comment -S -O binary out/kernel.ld out/kernel.bin

#制作内核映象文件

out/creat_img.exe:code/creat_img.c

   gpp code/creat_img.c -o out/creat_img.exe

#执行dos命令,在final目录下生成a.img文件

A:

   out/creat_img.exe final/a.img

 

#写入文件,argv[1]=目标文件 argv[2]=源文件 argv[3]=写入偏移量  

#DOS下用法: write.exe a.img kernelloader.bin 512

out/write_in_img.exe:code/write_in_img.c

   gpp code/write_in_img.c -o out/write_in_img.exe

#执行dos命令,向a.img写入代码,内容是boot.bin

#写入磁盘位置从0偏移量起始,1个扇区512字节

B:

   out/write_in_img.exe final/a.img out/boot.bin 0

#执行dos命令,向a.img写入代码,内容是kernelloader.bin

# boot.bin已经占用了512字节,写入磁盘位置从512偏移量起始,2个扇区1024字节

C:

   out/write_in_img.exe final/a.img out/kernelloader.bin 512

#执行dos命令,向a.img写入代码,内容是kernel.bin

# boot.bin+kernelloader.bin已经占用了512+1024 = 1536字节,写入磁盘位置从1536偏移量起始,占1个扇区512字节

D:

   out/write_in_img.exe final/a.img out/kernel.bin 1536

   

######################

 

 

 

运行模拟器,结果显示如图:

 

clip_image001

 

 

0 0