Chinaunix首页 | 论坛 | 博客
  • 博客访问: 48965
  • 博文数量: 29
  • 博客积分: 718
  • 博客等级: 上士
  • 技术积分: 320
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-15 20:11
文章分类

全部博文(29)

文章存档

2011年(29)

分类:

2011-05-08 16:51:52

最近在看linux 0.12源码,写了sprintk()格式化输出函数,本函数是一个可变参数函数,要对参数处理,必需知道,它们的个数和类型,那么这些函数是怎么知道参数个数,和类型的?其实这些可变参数个数和类型 ,函数能过前面的格式化控制字符获取。编写一个简单的例子可以看出,只输出了a的值。
        int a;
        int b;
        printf("%d",a,b);
格式化控制字符串是必需的,且必定位于第一个参数  printf("%d",a,"%d",b);后面的将会忽略,有了参数的个数和类型,还是不够的,我们还需要获取这些参数,在C中函数参数从右向左入栈的,也就是说这个格式化控制字符串指针,将在栈顶。这样所有问题都解决了。下面定义了几个宏,去访问参数。下面是具体的代码。
#define  __va_rounded_size(TYPE)\     //__cdecl(C语言默认的函数调用方法)入栈操作针对的是一个字
((sizeof(TYPE)+sizeof(int)-1)&~(sizeof(int)-1))
typedef char * va_list;
#define    va_start(AP,LASTARG)\//返回可变参数的第一个参数。
(AP=((char *)&(LASTARG)+__va_rounded_size(LASTARG)))
#define  va_end(AP)
#define  va_arg(AP,TYPE)\//返回AP所指的参数,并使AP指向下一个参数,
(AP+=__va_rounded_size(TYPE) , *((TYPE *)(AP-__va_rounded_size(TYPE))))
#define  MEMORY    1024//定义显存大小
//25*80字符模式
struct{
    int x;
    int y;
}cursor={0,0};//要显示字符的起始位置
void copyString(char **str1,char  *str2){//带指针移动的字符串
    while(*(*str1)++=*str2++);
    (*str1)--;
}
char * intToString(int val){ //把int转为字符串
    char intString[10];
    int  wei=1;
    int  tmp=val;
    int  num;
    while(tmp/=10){
          wei++;
    }
    wei--;
    do {
         num=val%10;
         intString[wei--]=num+'0';
     }
    while(val/=10);   
    return intString;
}
int  printk(const char *fmt){//在(cursor.x,cursor.y)打印字符串

    long  point=0;
    long  addr=(long)fmt;
    point=cursor.x*80+cursor.y;
    __asm__("push    %%gs\n\t" //把字符移动到0x20(显存段选择子),描述符0x00c0920b800000002
        "push    %%eax\n\t"
        "push    %%esi\n\t"
        "mov    $0x20,%%bx\n\t"
        "mov    %%bx,%%gs\n\t"
        "mov    %%edi,%%ebx\n\t"
       

        "getchar:movb    (%%esi),%%al\n\t"
        "cmp    $0,%%al\n\t"
        "je    end\n\t"
        "mov    $4,%%ah\n\t"
        "mov    %%ax,%%gs:(%%ebx)\n\t"
        "add    $0x02,%%ebx\n\t"
        "inc     %%si\n\t"
        "cmp    $4000,%%ebx\n\t"
        "jne    con\n\t"
           
        "pushl    %%eax\n\t"//超过最大行25行,上移一行。
        "pushl    %%esi\n\t"
        "pushl    %%edi\n\t"
        "pushl    %%ecx\n\t"
        "mov $0x20,%%ax\n\t"
        "mov %%ax,%%ds\n\t"
        "mov %%ax,%%es\n\t"
        "mov    $160,%%esi\n\t"
        "xor    %%edi,%%edi\n\t"           
        "mov_row:cld\n\t"
        "mov    $4000,%%cx\n\t"
        "rep    \n\t"
        "movsb\n\t"

        "popl    %%ecx\n\t"
        "popl     %%edi\n\t"
        "popl     %%esi\n\t"
        "mov    $0x10,%%ax\n\t"
        "mov    %%ax,%%ds\n\t"
        "popl     %%eax\n\t"
        "subl    $160,%%ebx\n\t"
       
        "con: jmp getchar\n\t"
        "end: popl    %%esi\n\t"
        "popl        %%eax\n\t"
        "pop     %%gs\n\t"
        :"=b"(point):"S" (addr),"D"(point<<1));
        cursor.x=point/160;//重新设置字符显示位置
        cursor.y=point%160/2;       
}

void sprintk(const char *fmt,...){//对可变参数进行处理,把要显示的转化为字符串,并调用printk显示
    char cach[MEMORY];//存放转化的字符串
    char *c=cach;
    va_list ap;
    char *p;
    int ival;
    char cval;
    char *sval;
    va_start(ap,fmt);
    for(p=fmt;*p;p++){
        if(*p!='%'){
          *c++=*p;
          continue;
        }
        switch(*++p){//只简单的实现%d,%c,%s更多实现自己添加
           case 'd':
            ival=va_arg(ap,int);
            copyString(&c,intToString(ival));
            break;
           case 'c':
            cval=va_arg(ap,char);
            *c++=cval;
            break;
           case 's':
            sval=va_arg(ap,char *);
            copyString(&c,sval);
            break;
           default:
            *c++=*p;
            break;
        }
    }   
    printk(cach);   
}
void  clear_scr(){//清屏

    __asm__("push    %%gs\n\t"
        "push    %%ebx\n\t"
        "mov    $0x20,%%ebx\n\t"
        "mov    %%bx,%%gs\n\t"
        "movl    $0x0,%%ebx\n\t"
        "mov    $0x0,%%ax\n\t"
        "lo:mov %%ax,%%gs:(%%ebx)\n\t"   
        "add    $0x02,%%ebx\n\t"
        "cmp    $4000,%%ebx\n\t"
        "jne    lo\n\t"
        "popl    %%ebx\n\t"
        "pop     %%gs"
        ::);
    cursor.x=0;
    cursor.y=0;
}


int main(){
 
    int a;
    char *s="This  is a  printk()";
    sprintk("%d  %s",a,s);
}
效果图如下

阅读(1898) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~