注:以下内容学习于韦东山老师arm裸机第一期视频教程
实现了简单的printf函数,可以实现 %d,%o,%u,%x,%c,%s格式的输出,具体代码如下,已对其中代码大部分进行注释
/* my_printf.c */ #include "my_printf.h" //================================================================================================== typedef char * va_list; /* va_start,va_arg, va_end,宏的用法在可变参数函数中有具体介绍*/ #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_arg(ap,t) ( *(t *)( ap=ap + _INTSIZEOF(t), ap- _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 ) //================================================================================================== unsigned char hex_tab[]={'0','1','2','3','4','5','6','7',\ '8','9','a','b','c','d','e','f'}; /* __out_putchar是宏定义putchar 实际会调用uart.c中的putchar函数来输出字符 */ static int outc(int c) { __out_putchar(c); return 0; } /* 输出字符串 */ static int outs (const char *s) { while (*s != '\0') __out_putchar(*s++); return 0; } /* 根据数字n, 进制base,前导码lead,最大长度maxwidth,构造需要得到的字符串 */ static int out_num(long n, int base,char lead,int maxwidth) { unsigned long m=0; /* 数字字符串的长度最大为MAX_NUMBER_BYTES=64个 */ /* s指向buf数组的最后 */ char buf[MAX_NUMBER_BYTES], *s = buf + sizeof(buf); int count=0,i=0; *--s = '\0'; /* 传入负值时取绝对值,最后在前面输出'-' */ if (n < 0){ m = -n; } else{ m = n; } /* 根据进制得到每一位,存放在s中 */ /* 权重低的会先存放在s中,存放完成之后s-- */ do{ *--s = hex_tab[m%base]; count++; /* 计算数字的个数 */ }while ((m /= base) != 0); /* 根据数字的最大长度设置前导码 */ if( maxwidth && count < maxwidth){ for (i=maxwidth - count; i; i--) *--s = lead; } if (n < 0) *--s = '-'; /* 输出数字字符串 */ return outs(s); } /*reference : int vprintf(const char *format, va_list ap); */ static int my_vprintf(const char *fmt, va_list ap) { char lead=' '; /* 前导码默认是空格 */ int maxwidth=0; /* 字符串的最大长度 */ /* 在fmt字符串中寻找到%符号 */ for(; *fmt != '\0'; fmt++) { if (*fmt != '%') { outc(*fmt); continue; } lead=' '; maxwidth=0; //format : %08d, %8d,%d,%u,%x,%f,%c,%s fmt++; /* fmt指向%的下一个字符 */ /* 判断前导码是不是'0' */ if(*fmt == '0'){ lead = '0'; fmt++; } /* 设置最大输出长度 */ while(*fmt >= '0' && *fmt <= '9'){ maxwidth *=10; maxwidth += (*fmt - '0'); fmt++; } /* 根据输出格式输出对应字符串 */ switch (*fmt) { case 'd': out_num(va_arg(ap, int), 10,lead,maxwidth); break; case 'o': out_num(va_arg(ap, unsigned int), 8,lead,maxwidth); break; case 'u': out_num(va_arg(ap, unsigned int), 10,lead,maxwidth); break; case 'x': out_num(va_arg(ap, unsigned int), 16,lead,maxwidth); break; case 'c': outc(va_arg(ap, int )); break; case 's': outs(va_arg(ap, char *)); break; default: outc(*fmt); break; } } return 0; } //reference : int printf(const char *format, ...); int printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); my_vprintf(fmt, ap); va_end(ap); return 0; } int my_printf_test(void) { printf("This is www.100ask.org my_printf test\n\r") ; printf("test char =%c,%c\n\r", 'A','a') ; printf("test decimal number =%d\n\r", 123456) ; printf("test decimal number =%d\n\r", -123456) ; printf("test hex number =0x%x\n\r", 0x55aa55aa) ; printf("test string =%s\n\r", "www.100ask.org") ; printf("num=%08d\n\r", 12345); printf("num=%8d\n\r", 12345); printf("num=0x%08x\n\r", 0x12345); printf("num=0x%8x\n\r", 0x12345); printf("num=0x%02x\n\r", 0x1); printf("num=0x%2x\n\r", 0x1); printf("num=%05d\n\r", 0x1); printf("num=%5d\n\r", 0x1); return 0; } /* my_printf.h */ #ifndef _MY_PRINTF_H #define _MY_PRINTF_H #include "uart.h" #define __out_putchar putchar #define MAX_NUMBER_BYTES 64 extern int my_printf_test(void); int printf(const char *fmt, ...); #endif /* _MY_PRINTF_H */ /* Makefile */ # 注意: 由于目前只用了4K的RAM,还没有学习使用内存,因此在编译出printf.bin文件之后会发现文件远大于4K # 查看反汇编代码可以发现,只读数据段与数据段之间差别较大,因此需要在Makefile中强制指定数据段的地址,根据自己实际的反汇编确定即可 objs = uart.o main.o start.o lib1funcs.o my_printf.o A = printf all:$(objs) arm-linux-ld -Ttext 0 -Tdata $^ -o $(A).elf #指定了代码段和数据段的地址 arm-linux-objcopy -O binary -S $(A).elf $(A).bin arm-linux-objdump -D $(A).elf > $(A).dis %.o:%.c arm-linux-gcc -c -o $@ $< %.o:%.S arm-linux-gcc -c -o $@ $< clean: rm *.o *.elf *.bin