| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1733 人关注过本帖, 1 人收藏
标题:一步步写操作系统之第二步:在内核添加屏幕输出函数
取消只看楼主 加入收藏
miaowangjian
Rank: 2
等 级:论坛游民
帖 子:34
专家分:30
注 册:2010-1-29
结帖率:100%
收藏(1)
 问题点数:0 回复次数:0 
一步步写操作系统之第二步:在内核添加屏幕输出函数
这一步,主要是一个过渡,为将来的工作做准备。
在这里的主要工作是整理代码与添加字符串与int型数值的输出函数。
在yc09中,编译后的二进制文件,函数似乎是按照在代码中的先后顺序排列的,然后再在末尾放置所有的变量。
在整个内核中,是以kernel.c文件为核心。在后面添加任何模块,都是在kernel.c文件的开头包含头文件,在kernel.c文件的末尾包含代码文件。如此,就可以保证内核的入口函数(main函数)一直是在最前面。
所有的头文件里,主要包含宏、数据结构定义、全局变量以及函数的声明等。
而对应的代码文件,则是具体的函数体。
下面是此次新添加或有过修改的代码,未作改动的文件将不再贴出。

code:kernel.h(新)
程序代码:
//文件:kernel.h
//功能:kernel头文件,放置需要用到的宏、数据结构定义、全局变量以及函数的声明等
//作者:miao
//时间:2010-5-14

/* 宏定义区 */
#define ProtecAddr 0x7f00 //进入保护模式后的程序基址
//GDT 选择子
#define SelectorCode32 8*1 //指向32位段处代码段,可执行可读
#define SelectorVideo  8*2 //指向显存首地址
#define SelectorData32 8*3 //指向32位段处,这样,在程序中的变量就可以读写了

/* 数据结构与全局变量定义区 */
//GDT界限,注意,这个与boot.c中的GDT不同,从boot.c跳转过来后会立即载入这个新的GDT
DESCRIPTOR label_gdt[] =
{
  //         段基址      段界限   属性
  Descriptor(0,                0, 0),
  Descriptor(ProtecAddr, 0xfffff, DA_CR | DA_32),  //32位代码段,可执行可读
  Descriptor(0xb8000,     0xffff, DA_DRW),         //显存地址段,可读可写
  Descriptor(ProtecAddr, 0xfffff, DA_DRW | DA_32), //令32位代码段的变量可以读写
};

#pragma pack(1)
struct GDT_PTR
{
  t_16 size;
  void *addr;
}GdtPtr = {sizeof(label_gdt), (char*)&label_gdt + ProtecAddr}; //段界限,基地址
#pragma pack()

int kernel_main();

code:kernel.c(改)
程序代码:
//文件:kernel.c  
//功能:内核程序,目前功能为测试print.c里的几个输出函数  
//运行:run.exe自动会编译boot.c与生成img并调用Bochs运行此程序。  
//作者:miao  
//时间:2010-5-14  
 
#define YCBIT 32  //告诉编译器,以32位格式编译程序  
#define YCORG 0x0 //此值会对在编译时对变量函数等产生地址基址偏移量,简单起便,设置为0  
#include "global.h"  
#include "kernel.h"  
#include "print.h"  
 
//内核入口点  
asm void main()  
{  
  lgdt  cs:GdtPtr           //加载新的GDTR  
 
  mov   eax, SelectorVideo  
  mov   gs, ax              //视频段选择子(目的)  
  mov   eax, SelectorData32 //令32位代码段的变量(printPlace)可以读写  
  mov   ds, ax  
 
  jmp  kernel_main  
}  
 
int kernel_main()  
{  
  int i;  
    
  //测试字符串输出函数  
  disp_str("this is default color string.\n");  
    
  for(i=0;i<=0x7f;i++)  
  {  
    disp_color_str("c ",i);  
  }  
  disp_str("\n");  
    
  for(i=0x80;i<=0xff;i++)  
  {  
    disp_color_str("c ",i);  
  }  
  disp_str("\n");  
    
  for(i=0;i<0x2f;i++)  
    disp_int(i);  
  disp_str("\n");  
    
  for(i=0xffff;i>0xffe0;i--)  
    disp_int(i);  
 
  while(1);  
  return 0;  
}  
#include "print.c"  


code:print.h(新)
程序代码:
//文件:print.h  
//功能:print头文件,放置需要用到的宏、数据结构定义、全局变量以及函数的声明等  
//作者:miao  
//时间:2010-5-14  
 
/* 数据结构与全局变量定义区 */ 
int disp_pos;//记录已经输出到屏幕哪里的坐标("光标")  
 
/* 函数的声明区 */ 
void disp_str(char * info);//使用蓝底红字输出一个字符串  
asm void disp_color_str(char * info, int color);//使用指定颜色输出一个字符串  
void disp_int(int input);//以十六进制格式输出一个数 
//文件:print.h
//功能:print头文件,放置需要用到的宏、数据结构定义、全局变量以及函数的声明等
//作者:miao
//时间:2010-5-14

/* 数据结构与全局变量定义区 */
int disp_pos;//记录已经输出到屏幕哪里的坐标("光标")

/* 函数的声明区 */
void disp_str(char * info);//使用蓝底红字输出一个字符串
asm void disp_color_str(char * info, int color);//使用指定颜色输出一个字符串
void disp_int(int input);//以十六进制格式输出一个数

code:print.c(新)
程序代码:
//文件:print.c  
//功能:向屏幕输出字符的相关函数  
//运行:run.exe自动会编译boot.c与生成img并调用Bochs运行此程序。  
//作者:miao  
//时间:2010-5-14  
 
/* 屏幕颜色与字符的显示(两字节大小) 
               高字节             低字节 
   7 6 5 4    3 2 1 0    7 6 5 4 3 2 1 0 
闪烁 R G B 高亮 R G B              ASCII 
  背景颜色   字符颜色               字符 
*/ 
void disp_str(char * info)  
{  
  disp_color_str(info,0x14);//默认蓝底红字  
}  
 
asm void disp_color_str(char * info, int color)  
{  
  push  ebp  
  mov   ebp, esp  
 
  mov   esi, [ebp + 8]   //info  
  mov   edi, disp_pos    //取上次输入完后的“光标”位置  
  mov   ah, [ebp + 0xc]  //color  
 
dpc_1:    
  lodsb                  //从esi指向的源地址中逐一读取一个字符送入al中  
  test  al, al           //al是否为空  
  jz    dpc_2            //为空(无字符串要输出),结束函数  
  cmp   al, 0Ah          //是回车吗?  
  jnz   dpc_3            //不是换行符,跳到dpc_3直接输出字符  
    
  push  eax              //保存ah里的字符颜色信息  
  mov   eax, edi         //求出出下一行起始位置坐标  
  mov   bl, 160  
  div   bl  
  and   eax, 0FFh  
  inc   eax  
  mov   bl, 160  
  mul   bl  
  mov   edi, eax  
  pop   eax  
  jmp   dpc_1  
dpc_3:  
  mov   gs:[edi], ax     //将ax的信息放到显存显示出来  
  add   edi, 2  
  jmp   dpc_1  
 
dpc_2:  
  mov   disp_pos, edi   //保存“光标”位置  
 
  pop   ebp  
  ret  
}  
 
char *itoa(char *str, int num)//数字前面的 0 不被显示出来, 比如 0000B800 被显示成 0xB800  
{  
  char *p = str;  
  char ch;  
  int i;  
  t_bool flag = FALSE;//标记是否将零输出,从高位起,若当前位不为0,标记为true,其后遇到0,都会输出  
 
  *p++ = '0';  
  *p++ = 'x';  
  if(num == 0)         //为零,以0x0形式输出  
    *p++ = '0';  
  else 
  {    
    for(i=28;i>=0;i-=4)//从十六进制高位起,逐个转换为字符形式  
    {  
      ch = (num >> i) & 0xF;  
      if(flag || (ch > 0))  
      {  
        flag = TRUE;  
        ch += '0';  
        if(ch > '9')   //为a,b,c,d,e,f  
          ch += 7;  
        *p++ = ch;  
      }  
    }  
  }  
  *p = 0;   
  return str;  
}  
 
//将数字转换为十六进制字符串显示出来  
void disp_int(int num)  
{  
  char output[16];  
  itoa(output, num);  
  disp_str(output);  
}  
搜索更多相关主题的帖子: 操作系统 内核 函数 屏幕 输出 
2010-05-17 05:24
快速回复:一步步写操作系统之第二步:在内核添加屏幕输出函数
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.042139 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved