由上一篇文章看到,所写的内核的代码只存在于两个文件中,汇编写的一个,C写的一个,看起来乱得紧,改起来忒累人了。何况以后要写的代码就更多了,不可能老推在一起,得给它们分分家。
首先按代码的性质分,一是一些常量啊,数据结构的定义啊,一些函数的声明啊等等,这些东东可以建一个文件夹来存放,命名为include;还有一些工具代码,如果显示个字符串之类的函数,可以建立一个lib的文件夹来存放它们;其它的就是一些跟内核相关的代码,如设置8259A,填充描述符的函数啊等等,就把它们把在一个kernel文件夹吧。至于前面的boot和loader等其它相关头文件全部放在boot文件夹里,这里不再赘述。
首先从最简单的开始,从lib文件夹入手,首先把用汇编写的子程序拿出来放到一起,建立一个kliba.asm文件:
;=====================================================================
; kliba.asm
;=====================================================================
extern Disp_Pos
[section .text]
global Out_Byte
global Disp_Color_Str
;Out_Byte=============================================================
;void Out_Byte(u16 port,u8 value);
Out_Byte:
push ebp
mov ebp,esp
push eax
push edx
mov edx,[ebp + 8]
mov eax,[ebp + 12]
out dx,al
nop
nop
pop edx
pop eax
pop ebp
ret
;end of Out_Byte======================================================
;Disp_Color_Str=======================================================
;函数原型:void Disp_Color_Str(char * pszStr,u32 color);
;函数功能:在Disp_Str的基础上改变字符串的颜色
Disp_Color_Str:
push ebp
mov ebp,esp
push eax
push ebx
push esi
push edi
mov esi,[ebp + 8] ;取得要显示的字符串的偏移
mov edi,[Disp_Pos]
xor eax,eax
mov eax,[ebp + 12]
shl eax,8
and eax,0000ff00h ;ah保存颜色
.begin:
lodsb
test al,al ;如果字符为0就退出
je .exit
cmp al,0ah ;如果是回车则跳到改变esi跳到下一行
jne .disp
push eax
mov eax,edi
mov bl,160
div bl
and ax,0ffh
inc ax
mov bl,160
mul bl
mov edi,eax
pop eax
jmp .begin
.disp:
mov [gs:edi],ax
add edi,2
jmp .begin
.exit:
mov [Disp_Pos],edi ;全局变量赋回值
pop edi
pop esi
pop ebx
pop eax
pop ebp
ret
;end of Disp_Color_Str================================================
除此之外,还有一些用C写的函数,放到klib.c文件中,其中包含了一些头文件,待会儿再解释:
- /*====================================================================
- klib.c
- ====================================================================*/
- #include "type.h"
- #include "proto.h"
- /*======================================================================*
- itoa
- *======================================================================*/
- /* 数字前面的 0 不被显示出来, 比如 0000B800 被显示成 B800 */
- char * itoa(char * str, int num)
- {
- char *p = str;
- char ch;
- int i;
- int flag = 0;
- *p++ = '0';
- *p++ = 'x';
- if(num == 0){
- *p++ = '0';
- }
- else{
- for(i=28;i>=0;i-=4){
- ch = (num >> i) & 0xF;
- if(flag || (ch > 0)){
- flag = 1;
- ch += '0';
- if(ch > '9'){
- ch += 7;
- }
- *p++ = ch;
- }
- }
- }
- *p = 0;
- return str;
- }
- /*======================================================================*
- Disp_Int
- *======================================================================*/
- void Disp_Int(u32 num)
- {
- char output[16];
- itoa(output, num);
- Disp_Color_Str(output,0xc);
- }
搞定了lib,再来到include文件夹,主要是定义一些头文件。我们定义了一些类型,如u32,u16等,我们建立一个type.h文件:
- /*====================================================================
- type.h
- ====================================================================*/
- #ifndef _TYPE_H_
- #define _TYPE_H_
- typedef unsigned char u8;
- typedef unsigned short u16;
- typedef unsigned int u32;
- typedef void (*Int_Handler)();
- #endif
接下来是一些函数的声明,放到proto.h文件中:
- /*====================================================================
- proto.h
- ====================================================================*/
- #ifndef _PROTO_H_
- #define _PROTO_H_
- void Disp_Color_Str(char *p_Str,u32 color);
- void Disp_Int(u32 num);
- void Out_Byte(u16 port,u8 value);
- void Fill_Desc(u8 desc_no,u32 base,u32 limit,u16 attr);
- void Fill_Gate(u8 idt_no,Int_Handler handler,u8 type,u8 privilege);
- void Init_GDT();
- void Init_IDT();
- void Init_8259A();
- void Exception_Handler(u32 vec_no,u32 err_code,u32 eip,u32 cs,int eflags);
- void IRQ_Handler(u32 irq_no);
- /* 中断处理函数 */
- void Divide_Error();
- void Single_Step_Exception();
- void NMI();
- void Breakpoint_Exception();
- void Overflow();
- void Bounds_Check();
- void Inval_Opcode();
- void Copr_Not_Available();
- void Double_Fault();
- void Copr_Seg_Overrun();
- void Inval_TSS();
- void Segment_Not_Present();
- void Stack_Exception();
- void General_Protection();
- void Page_Fault();
- void Copr_Error();
- void HW_Int_00();
- void HW_Int_01();
- void HW_Int_02();
- void HW_Int_03();
- void HW_Int_04();
- void HW_Int_05();
- void HW_Int_06();
- void HW_Int_07();
- void HW_Int_08();
- void HW_Int_09();
- void HW_Int_10();
- void HW_Int_11();
- void HW_Int_12();
- void HW_Int_13();
- void HW_Int_14();
- void HW_Int_15();
- #endif
接下来是定义一些与保护模式相关的常量与数据结构声明,放在protect.h中:
- /*====================================================================
- protect.h
- ====================================================================*/
- #ifndef _PROTECT_H_
- #define _PROTECT_H_
- /* 描述符类型值说明 */
- #define DA_32 0x4000 /* 32 位段 */
- #define DA_LIMIT_4K 0x8000 /* 段界限粒度为 4K 字节 */
- #define DA_DPL0 0x00 /* DPL = 0 */
- #define DA_DPL1 0x20 /* DPL = 1 */
- #define DA_DPL2 0x40 /* DPL = 2 */
- #define DA_DPL3 0x60 /* DPL = 3 */
- /* 存储段描述符类型值说明 */
- #define DA_DR 0x90 /* 存在的只读数据段类型值 */
- #define DA_DRW 0x92 /* 存在的可读写数据段属性值 */
- #define DA_DRWA 0x93 /* 存在的已访问可读写数据段类型值 */
- #define DA_C 0x98 /* 存在的只执行代码段属性值 */
- #define DA_CR 0x9A /* 存在的可执行可读代码段属性值 */
- #define DA_CCO 0x9C /* 存在的只执行一致代码段属性值 */
- #define DA_CCOR 0x9E /* 存在的可执行可读一致代码段属性值 */
- /* 系统段描述符类型值说明 */
- #define DA_LDT 0x82 /* 局部描述符表段类型值 */
- #define DA_TaskGate 0x85 /* 任务门类型值 */
- #define DA_386TSS 0x89 /* 可用 386 任务状态段类型值 */
- #define DA_386CGate 0x8C /* 386 调用门类型值 */
- #define DA_386IGate 0x8E /* 386 中断门类型值 */
- #define DA_386TGate 0x8F /* 386 陷阱门类型值 */
- /* 权限 */
- #define PRIVILEGE_KRNL 0
- #define PRIVILEGE_TASK 1
- #define PRIVILEGE_USER 3
- /* 中断向量 */
- #define INT_VECTOR_DIVIDE 0x0
- #define INT_VECTOR_DEBUG 0x1
- #define INT_VECTOR_NMI 0x2
- #define INT_VECTOR_BREAKPOINT 0x3
- #define INT_VECTOR_OVERFLOW 0x4
- #define INT_VECTOR_BOUNDS 0x5
- #define INT_VECTOR_INVAL_OP 0x6
- #define INT_VECTOR_COPROC_NOT 0x7
- #define INT_VECTOR_DOUBLE_FAULT 0x8
- #define INT_VECTOR_COPROC_SEG 0x9
- #define INT_VECTOR_INVAL_TSS 0xA
- #define INT_VECTOR_SEG_NOT 0xB
- #define INT_VECTOR_STACK_FAULT 0xC
- #define INT_VECTOR_PROTECTION 0xD
- #define INT_VECTOR_PAGE_FAULT 0xE
- #define INT_VECTOR_COPROC_ERR 0x10
- typedef struct s_descriptor
- {
- u16 limit_low; /* limit(0..15) */
- u16 base_low; /* base(0..15) */
- u8 base_mid; /* base(16..23) */
- u8 attr1; /* P(1) DPL(2) S(1) TYPE(4)*/
- u8 limit_high_attr2; /* G(1) D/B(1) O(1) AVL(1) limit(16..19) */
- u8 base_high; /* base(24..31) */
- }Descriptor;
- typedef struct s_gate
- {
- u16 offset_low; /* offset(0..15) */
- u16 selector; /* selector */
- u8 dcount;
- u8 attr; /* P(1) DPL(2) S(1) TYPE(4) */
- u16 offset_high;
- }Gate;
- #endif
我们还想把全局变量分开来放,所以还得建立一个global.h文件来存放全局变量的声明:
- /*====================================================================
- global.h
- ====================================================================*/
- #ifndef _GLOBAL_H_
- #define _GLOBAL_H_
- extern Descriptor GDT[128];
- extern u8 GDT_Ptr[6];
- extern Gate IDT[256];
- extern u8 IDT_Ptr[6];
- extern u32 Disp_Pos;
- #endif
好了,include文件夹的内容也完成了,最后来到kernel文件夹。
先把初始化8259A的代码和外中断处理代码抽取出来,形成i8259.c:
- /*====================================================================
- i8259.c
- ====================================================================*/
- #include "type.h"
- #include "proto.h"
- void Init_8259A()
- {
- Out_Byte(0x20,0x11);
- Out_Byte(0xa0,0x11);
- Out_Byte(0x21,0x20);
- Out_Byte(0xa1,0x28);
- Out_Byte(0x21,0x4);
- Out_Byte(0xa1,0x2);
- Out_Byte(0x21,0x1);
- Out_Byte(0xa1,0x1);
- Out_Byte(0x21,0xfd);
- Out_Byte(0xa1,0xff);
- }
- void IRQ_Handler(u32 irq_no)
- {
- Disp_Color_Str("IRQ_NO:",0xd);
- Disp_Int(irq_no);
- Disp_Color_Str("/n",0xd);
- }
接着把与保护模式的相关代码聚集起来形成protect.c:
- /*====================================================================
- protect.c
- ====================================================================*/
- #include "type.h"
- #include "proto.h"
- #include "protect.h"
- #include "global.h"
- void Fill_Desc(u8 desc_no,u32 base,u32 limit,u16 attr)
- {
- Descriptor *p_Desc = (Descriptor *)&GDT[desc_no];
- p_Desc->limit_low = limit & 0xffff;
- p_Desc->base_low = base & 0xffff;
- p_Desc->base_mid = (base >> 16) & 0xff;
- p_Desc->attr1 = attr & 0xff;
- p_Desc->limit_high_attr2 = ((limit >> 16) & 0xf) | (attr >> 8);
- p_Desc->base_high = (base >> 24) & 0xff;
- }
- void Init_GDT()
- {
- Fill_Desc(0,0,0,0);
- Fill_Desc(1,0,0xfffff,DA_DRW | DA_32 | DA_LIMIT_4K);
- Fill_Desc(2,0,0xfffff,DA_C | DA_32 | DA_LIMIT_4K);
- Fill_Desc(3,0xb8000,0xffff,DA_DRW);
- }
- void Fill_Gate(u8 idt_no,Int_Handler handler,u8 type,u8 privilege)
- {
- Gate *p_Gate = (Gate*)&IDT[idt_no];
- u32 base = (u32)handler;
- p_Gate->offset_low = base & 0xffff;
- p_Gate->selector = 16;
- p_Gate->dcount = 0;
- p_Gate->attr = type | (privilege << 5);
- p_Gate->offset_high = (base >> 16) & 0xffff;
- }
- void Init_IDT()
- {
- Fill_Gate(INT_VECTOR_DIVIDE,Divide_Error,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_DEBUG,Single_Step_Exception,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_NMI,NMI,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_BREAKPOINT,Breakpoint_Exception,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_OVERFLOW,Overflow,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_BOUNDS,Bounds_Check,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_INVAL_OP,Inval_Opcode,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_COPROC_NOT,Copr_Not_Available,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_DOUBLE_FAULT,Double_Fault,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_COPROC_SEG,Copr_Seg_Overrun,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_INVAL_TSS,Inval_TSS,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_SEG_NOT,Segment_Not_Present,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_STACK_FAULT,Stack_Exception,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_PROTECTION,General_Protection,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_PAGE_FAULT,Page_Fault,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(INT_VECTOR_COPROC_ERR,Copr_Error,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x20,HW_Int_00,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x21,HW_Int_01,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x22,HW_Int_02,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x23,HW_Int_03,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x24,HW_Int_04,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x25,HW_Int_05,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x26,HW_Int_06,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x27,HW_Int_07,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x28,HW_Int_08,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x29,HW_Int_09,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x2a,HW_Int_10,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x2b,HW_Int_11,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x2c,HW_Int_12,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x2d,HW_Int_13,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x2e,HW_Int_14,DA_386IGate,PRIVILEGE_KRNL);
- Fill_Gate(0x2f,HW_Int_15,DA_386IGate,PRIVILEGE_KRNL);
- }
- void Exception_Handler(u32 vec_no,u32 err_code,u32 eip,u32 cs,int eflags)
- {
- int i;
- Disp_Pos = 0;
- for(i = 0 ; i < 80 * 5 ; i++)
- {
- Disp_Color_Str(" ",0xc);
- }
- Disp_Pos = 0;
- Disp_Color_Str("VEC_NO:",0xc);
- Disp_Int(vec_no);
- Disp_Color_Str("/n",0xc);
- Disp_Color_Str("ERROR_CODE:",0xc);
- Disp_Int(err_code);
- Disp_Color_Str("/n",0xc);
- Disp_Color_Str("CS:",0xc);
- Disp_Int(cs);
- Disp_Color_Str("/n",0xc);
- Disp_Color_Str("EIP:",0xc);
- Disp_Int(eip);
- Disp_Color_Str("/n",0xc);
- Disp_Color_Str("EFLAGS:",0xc);
- Disp_Int(eflags);
- Disp_Color_Str("/n",0xc);
- }
接下来是把全部变量的定义放在global.c中:
- /*====================================================================
- global.c
- ====================================================================*/
- #include "type.h"
- #include "protect.h"
- Descriptor GDT[128];
- u8 GDT_Ptr[6];
- Gate IDT[256];
- u8 IDT_Ptr[6];
- u32 Disp_Pos;
接着是大管家start.c文件,它负责调用其它的函数:
- /*====================================================================
- start.c
- ====================================================================*/
- #include "type.h"
- #include "proto.h"
- #include "protect.h"
- #include "global.h"
- void C_Start()
- {
- Init_8259A();
- Init_GDT();
- Init_IDT();
- *(u16*)(&GDT_Ptr[0]) = 4 * 8 - 1;
- *(u32*)(&GDT_Ptr[2]) = (u32)&GDT;
- *(u16*)(&IDT_Ptr[0]) = sizeof(Gate) * 256 - 1;
- *(u32*)(&IDT_Ptr[2]) = (u32)&IDT;
- }
最后是kernel.asm文件:
extern GDT_Ptr
extern IDT_Ptr
extern Disp_Pos
extern C_Start
extern Exception_Handler
extern IRQ_Handler
Selector_Loader_Flat_RW equ 8
Selector_Kernel_Flat_RW equ 8
Selector_Kernel_Flat_C equ 16
Selector_Kernel_Video equ 24
[section .bss]
Stack_Space resb 2 * 1024
Top_Of_Stack:
[section .text]
global Divide_Error
global Single_Step_Exception
global NMI
global Breakpoint_Exception
global Overflow
global Bounds_Check
global Inval_Opcode
global Copr_Not_Available
global Double_Fault
global Copr_Seg_Overrun
global Inval_TSS
global Segment_Not_Present
global Stack_Exception
global General_Protection
global Page_Fault
global Copr_Error
global HW_Int_00
global HW_Int_01
global HW_Int_02
global HW_Int_03
global HW_Int_04
global HW_Int_05
global HW_Int_06
global HW_Int_07
global HW_Int_08
global HW_Int_09
global HW_Int_10
global HW_Int_11
global HW_Int_12
global HW_Int_13
global HW_Int_14
global HW_Int_15
global Out_Byte
global Disp_Color_Str
global _start
_start:
mov ax,Selector_Loader_Flat_RW
mov ds,ax
mov es,ax
call C_Start
lgdt [GDT_Ptr]
lidt [IDT_Ptr]
mov ax,Selector_Kernel_Flat_RW
mov ds,ax
mov es,ax
mov ss,ax
mov esp,Top_Of_Stack
mov ax,Selector_Kernel_Video
mov gs,ax
jmp Selector_Kernel_Flat_C:_test
_test:
;jmp 100:0
sti
hlt
Divide_Error:
push 0ffffffffh
push 0
jmp Exception
Single_Step_Exception:
push 0ffffffffh
push 1
jmp Exception
NMI:
push 0ffffffffh
push 2
jmp Exception
Breakpoint_Exception:
push 0ffffffffh
push 3
jmp Exception
Overflow:
push 0ffffffffh
push 4
jmp Exception
Bounds_Check:
push 0ffffffffh
push 5
jmp Exception
Inval_Opcode:
push 0ffffffffh
push 6
jmp Exception
Copr_Not_Available:
push 0ffffffffh
push 7
jmp Exception
Double_Fault:
push 8
jmp Exception
Copr_Seg_Overrun:
push 0ffffffffh
push 9
jmp Exception
Inval_TSS:
push 10
jmp Exception
Segment_Not_Present:
push 11
jmp Exception
Stack_Exception:
push 12
jmp Exception
General_Protection:
push 13
jmp Exception
Page_Fault:
push 14
jmp Exception
Copr_Error:
push 0ffffffffh
push 16
jmp Exception
Exception:
call Exception_Handler
add esp,8
hlt
HW_Int_00:
push 0;
call IRQ_Handler
add esp,4
hlt
HW_Int_01:
push 1;
call IRQ_Handler
add esp,4
hlt
HW_Int_02:
push 2;
call IRQ_Handler
add esp,4
hlt
HW_Int_03:
push 3;
call IRQ_Handler
add esp,4
hlt
HW_Int_04:
push 4;
call IRQ_Handler
add esp,4
hlt
HW_Int_05:
push 5;
call IRQ_Handler
add esp,4
hlt
HW_Int_06:
push 6;
call IRQ_Handler
add esp,4
hlt
HW_Int_07:
push 7;
call IRQ_Handler
add esp,4
hlt
HW_Int_08:
push 8;
call IRQ_Handler
add esp,4
hlt
HW_Int_09:
push 9;
call IRQ_Handler
add esp,4
hlt
HW_Int_10:
push 10;
call IRQ_Handler
add esp,4
hlt
HW_Int_11:
push 11;
call IRQ_Handler
add esp,4
hlt
HW_Int_12:
push 12;
call IRQ_Handler
add esp,4
hlt
HW_Int_13:
push 13;
call IRQ_Handler
add esp,4
hlt
HW_Int_14:
push 14;
call IRQ_Handler
add esp,4
hlt
HW_Int_15:
push 15;
call IRQ_Handler
add esp,4
hlt
真累啊,终于搞定了,让我们来编译链接一下吧,假定我们在各子目录的父目录:
nasm -f elf -o ./kernel/kernel.o ./kernel/kernel.asm
nasm -f elf -o ./lib/kliba.o ./lib/kliba.asm
gcc -c -I ./include -o ./kernel/i8259.o ./kernel/i8259.c
gcc -c -I ./include -o ./kernel/protect.o ./kernel/protect.c
gcc -c -I ./include -o ./kernel/global.o ./kernel/global.c
gcc -c -I ./include -o ./kernel/start.o ./kernel/start.c
gcc -c -I ./include -o ./lib/klib.o ./kernel/i8259.c
ld -s -Ttext 0x30400 -o kernel.bin ./kernel/kernel.o ./kernel/start.o ./kernel/i8259.o /
./kernel/protect.o ./kernel/global.o ./lib/kliba.o ./lib/klib.o
我们可以看到,为了编译链接一个kernel.bin,就要写那么多条命令,要是在调试过程中哪里出错了要重新编译链接,每一次都要输入那么多条命令简直就是灾难,况且我们的胎盘操作系统的路还没走多少,以后随着代码量的增大,要建立更多的文件和文件夹,你自己可以想象一下这个工作量了。
不过,还有MAKEFILE这个强大的工具可以供我们使用,我们只需要写个MAKEFILE,就可以轻松的让计算机帮我们完成这个龌龊繁琐的任务,明天再来研究,上床手机MOP咯。