第五章相应的汇编转换成C的分析【一】

    在上篇文章中,全部代码都用汇编实现,全部代码都挤在一堆,实在有点混乱,我们手中还有C这个武器,它的可读性无疑比汇编高得多。C当然不能完全取代汇编,但相当大的程度上是能使用C的。

    下面来分析一下在哪些内容能用C取代汇编的,一开始的段描述符(Descriptor)和门描述符(Gate)当然能用C的结构来描述,就不用写汇编里的宏,这无疑是一个进化;GDT和IDT可以用C来描述,不就是一个Descriptor的数组和一个Gate的数组吗?要填充的GDTR和IDTR的结构更简单,不就是两个6字节的数组吗?下面让我们来一点一点的实现。

    Descriptor和Gate的结构的C描述(根据下面两张图的结构得出):

Code:
  1. typedef struct s_descriptor   
  2. {   
  3.     u16 limit_low;                      /* limit(0..15) */  
  4.     u16 base_low;                       /* base(0..15) */  
  5.     u8  base_mid;                       /* base(16..23) */  
  6.     u8  attr1;                          /* P(1) DPL(2) S(1) TYPE(4)*/  
  7.     u8  limit_high_attr2;               /* G(1) D/B(1) O(1) AVL(1) limit(16..19) */  
  8.     u8  base_high;                      /* base(24..31) */  
  9. }Descriptor;   
  10.   
  11. typedef struct s_gate   
  12. {   
  13.     u16 offset_low;                     /* offset(0..15) */  
  14.     u16 selector;                       /* selector */  
  15.     u8  dcount;    
  16.     u8  attr;                           /* P(1) DPL(2) S(1) TYPE(4) */  
  17.     u16 offset_high;                    /* offset(16..31) */                                                       
  18. }Gate;  

    

    要填充的GDTR和IDTR的数组和GDT,IDT如下:

Code:
  1. u8 GDT_Ptr[6];   
  2. u8 IDT_Ptr[6];  
  3. Descriptor GDT[128];
  4. Gate IDT[256];

    上面的u8,u16可能你已经猜得出是什么意思了,就是一些typedef,目的是让我们一眼就看出来一个数是几位的:

Code:
  1. typedef unsigned char  u8;   
  2. typedef unsigned short u16;  
  3. typedef unsigned int   u32;  

   

    接下来就是要写填充GDT和IDT的代码了,在汇编中我们可以直接用宏填充,这里我们用C来写,就不能直接填充了,除非在C中也用宏就可以。不过我不太喜欢用宏,就把它抽象成一个函数吧,入口参数有一下几个,要填充的GDT的偏移号;段基址,段界限,段属性。

Code:
  1. void Fill_Desc(u8 desc_no,u32 base,u32 limit,u16 attr)   
  2. {   
  3.     Descriptor *p_Desc = (Descriptor *)&GDT[desc_no];   
  4.     p_Desc->limit_low = limit & 0xffff;   
  5.     p_Desc->base_low = base & 0xffff;   
  6.     p_Desc->base_mid = (base >> 16) & 0xff;   
  7.     p_Desc->attr1 = attr & 0xff;   
  8.     p_Desc->limit_high_attr2 = ((limit >> 16) & 0xf) | (attr >> 8);   
  9.     p_Desc->base_high = (base >> 24) & 0xff;   
  10. }  

     OK,今天的工作到此为止,呵呵,慢慢来嘛,离这学期结束还有一段时候嘛,饭要一口一口吃。到目前为止,我们想验证一下我们的工作成果,那就马上写一个来看看。要说明的一点是,我们先写出合格代码,然后再合理的组织它们。

kernel.asm:

extern GDT_Ptr
extern C_Start

Selector_Loader_Flat_RW  equ  8

Selector_Kernel_Flat_RW   equ   8
Selector_Kernel_Flat_C  equ  16
Selector_Kernel_Video   equ  24

[section .text]

global _start

_start:
 mov ax,Selector_Loader_Flat_RW
 mov ds,ax
 mov es,ax
 
 call C_Start
 
 lgdt [GDT_Ptr]
 
 jmp Selector_Flat_C:_test
 
_test:
 mov ax,Selector_Kernel_Video
 mov gs,ax
 mov ah,0ch
 mov al,'V'
 mov [gs:(80 * 20) * 2],ax
 hlt

start.c:

Code:
  1.   
  2. typedef unsigned char  u8;   
  3. typedef unsigned short u16;  
  4. typedef unsigned int   u32;   
  5.   
  6. /* 描述符类型值说明 */  
  7. #define DA_32               0x4000              /* 32 位段              */     
  8. #define DA_LIMIT_4K         0x8000              /* 段界限粒度为 4K 字节   */   
  9. #define DA_DPL0             0x00                /* DPL = 0              */   
  10. #define DA_DPL1             0x20                /* DPL = 1              */   
  11. #define DA_DPL2             0x40                /* DPL = 2              */   
  12. #define DA_DPL3             0x60                /* DPL = 3              */   
  13.        
  14. /* 存储段描述符类型值说明 */  
  15. #define DA_DR               0x90                /* 存在的只读数据段类型值          */   
  16. #define DA_DRW              0x92                /* 存在的可读写数据段属性值        */   
  17. #define DA_DRWA             0x93                /* 存在的已访问可读写数据段类型值   */   
  18. #define DA_C                0x98                /* 存在的只执行代码段属性值        */   
  19. #define DA_CR               0x9A                /* 存在的可执行可读代码段属性值     */   
  20. #define DA_CCO              0x9C                /* 存在的只执行一致代码段属性值     */   
  21. #define DA_CCOR             0x9E                /* 存在的可执行可读一致代码段属性值 */   
  22.        
  23. /* 系统段描述符类型值说明 */  
  24. #define DA_LDT              0x82                /* 局部描述符表段类型值           */   
  25. #define DA_TaskGate         0x85                /* 任务门类型值                  */    
  26. #define DA_386TSS           0x89                /* 可用 386 任务状态段类型值      */   
  27. #define DA_386CGate         0x8C                /* 386 调用门类型值              */   
  28. #define DA_386IGate         0x8E                /* 386 中断门类型值              */   
  29. #define DA_386TGate         0x8F                /* 386 陷阱门类型值              */   
  30.   
  31. typedef struct s_descriptor   
  32. {   
  33.     u16 limit_low;                      /* limit(0..15) */  
  34.     u16 base_low;                       /* base(0..15) */  
  35.     u8  base_mid;                       /* base(16..23) */  
  36.     u8  attr1;                          /* P(1) DPL(2) S(1) TYPE(4)*/  
  37.     u8  limit_high_attr2;               /* G(1) D/B(1) O(1) AVL(1) limit(16..19) */  
  38.     u8  base_high;                      /* base(24..31) */  
  39. }Descriptor;   
  40.   
  41. Descriptor GDT[128];   
  42. u8 GDT_Ptr[6];   
  43.   
  44. void Fill_Desc(u8 desc_no,u32 base,u32 limit,u16 attr)   
  45. {   
  46.     Descriptor *p_Desc = (Descriptor *)&GDT[desc_no];   
  47.     p_Desc->limit_low = limit & 0xffff;   
  48.     p_Desc->base_low = base & 0xffff;   
  49.     p_Desc->base_mid = (base >> 16) & 0xff;   
  50.     p_Desc->attr1 = attr & 0xff;   
  51.     p_Desc->limit_high_attr2 = ((limit >> 16) & 0xf) | (attr >> 8);   
  52.     p_Desc->base_high = (base >> 24) & 0xff;   
  53. }   
  54.   
  55. void Init_GDT()   
  56. {   
  57.     Fill_Desc(0,0,0,0);   
  58.     Fill_Desc(1,0,0xfffff,DA_DRW | DA_32 | DA_LIMIT_4K);   
  59.     Fill_Desc(2,0,0xfffff,DA_C | DA_32 | DA_LIMIT_4K);   
  60.     Fill_Desc(3,0xb8000,0xffff,DA_DRW);   
  61. }   
  62.   
  63. void C_Start()   
  64. {   
  65.     Init_GDT();   
  66.     *(u16*)(&GDT_Ptr[0]) = 4 * 8;   
  67.     *(u32*)(&GDT_Ptr[2]) = (u32)&GDT;   
  68. }   

 

  编译链接方法:nasm -f elf -o kernel.o kernel.asm

                   gcc -c -o start.o start.c  

                   ld -s -Ttext 0x30400 -o kernel.bin kernel.o start.o

    我们仍然是输出一个字母,这次选择的是V,呵呵,表示胜利。

    最后照例是附图一张,看看通过C来设置GDT是否正确。

你可能感兴趣的:(c,工作,汇编,gcc,任务,Descriptor)