最近在看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);
}
效果图如下
阅读(1857) | 评论(0) | 转发(0) |