printf 함수는 다양한 포멧으로 출력을 하는 만큼 복잡해서 여러 단계로 만듬
우선 debug_printf 부터
lib/stdio.h에 debug_printf 함수 추가
...는 가변인자
#ifndef LIB_STDIO_H_
#define LIB_STDIO_H_
uint32_t putstr(const char* s);
uint32_t debug_printf(const char* format, ...);
#endif
lib/stdio.c에 debug_printf 함수 구현
가변인자 처리를 위한 va_list, va_start, va_end가 추가되고
형식 지정자 처리는 vsprintf에서 수행
#include <stdint.h>
#include "HalUart.h"
#include "stdio.h"
uint32_t putstr(const char* s)
{
uint32_t c =0;
while(*s)
{
Hal_uart_put_char(*s++);
c++;
}
return c;
}
uint32_t debug_printf(const char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(printf_buf, format, args);
va_end(args);
return putstr(printf_buf);
}
include/stdarg.h 에
가변인자 처리를 위한 컴파일러 빌트인 함수들을 재정의
va_ ~ 는 stdio lib가 아니라 컴파일러 빌트인 함수
#ifndef INCLUDE_STDARG_H_
#define INCLUDE_STDARG_H_
typedef __builtin_va_list va_list;
#define va_start(v, l) __builtin_va_start(v,l)
#define va_end(v) __builtin_va_end(v)
#define va_arg(v,l) __builtin_va_arg(v,l)
#endif
이제 vsprintf를 구현하면 먼저 lib/stdio.h
#ifndef LIB_STDIO_H_
#define LIB_STDIO_H_
uint32_t putstr(const char* s);
uint32_t debug_printf(const char* format, ...);
uint32_t vsprintf(char* buf, const char* format, va_list arg);
#endif
lib/stdio.c에 vsprintf 구현
%c, &s, %u, %x 기능 제공
uint32_t vsprintf(char* buf, const char* format, va_list arg)
{
uint32_t c =0;
char ch;
char* str;
uint32_t uint;
uint32_t hex;
for(uint32_t i=0; format[i] ; i++)
{
if(format[i] == '%')
{
i++;
switch(format[i])
{
case 'c':
ch = (chat)va_arg(arg,int32_t);
buf[c++] = ch;
break;
case 's':
str = (char*)va_arg(arg, char*);
if (str == NULL)
{
str = "(NULL)";
}
while(*str)
{
buf[c++] = (*str++);
}
break;
case 'u':
uint = (uint32_t)va_arg(arg, uint32_t);
c += utoa(&buf[c], uint, utoa_dec);
break;
case 'x':
hex = (uint32_t)va_arg(arg, uint32_t);
c += utoa(&buf[c], hex, utoa_hex);
break;
}
}
else
{
buf[c++] = format[i];
}
}
if (c >= PRINTF_BUF_LEN)
{
buf[0] = '\0';
return 0;
}
buf[c] = '\0';
return c;
}
%u, %x에서 utoa 함수를 쓰고 있는데
말그대로 부호없는 정수를 문자열로 바꾸는 내용
lib/stdio.h 에 선언부터
#ifndef LIB_STDIO_H_
#define LIB_STDIO_H_
#include "stdarg.h"
typedef enum utoa_t
{
utoa_dec = 10,
utoa_hex = 16
}utoa_t;
uint32_t putstr(const char* s);
uint32_t debug_printf(const char* format, ...);
uint32_t vsprintf(char* buf, const char* format, va_list arg);
uint32_t utoa(char* buf, uint32_t val, utoa_t base);
#endif
중간에 t >= 10인 경우는 base가 16일때 11 ~ 15 인 경우이므로
ABCDEF로 만드는 거 때문에
'0'을 어짜피 뺏다 다시 더하므로 무시해서 보면
'A'를 기준으로 A는 10일 떄이므로 -10했을떄
t=11이라면 t = 1이 될것이고
'A' + 1 = 'B'로 출력하라는 식인거같다.
말이 좀 이상한데 16진수 부분 출력하기 위해 이렇게 표현한거같다.
uint32_t utoa(char* buf, uint32_t val, utoa_t base)
{
uint32_t c = 0;
int32_t idx = 0;
char tmp[11];
do
{
uint32_t t =val % (uint32_t)base;
if(t >= 10)
{
t += 'A' - '0' - 10;
}
tmp[idx] = (t + '0');
val /= base;
idx++;
}while(val);
//reverse
idx--;
while (idx > =0)
{
buf[c++] = tmp[idx];
idx--;
}
return c;
}
테스트 코드 작성하고
#include <stdint.h>
#include "HalUart.h"
#include "stdio.h"
static void Hw_init(void);
static void Printf_test(void);
void main(void)
{
Hw_init();
uint32_t i = 100;
while(i--)
{
Hal_uart_put_char('N');
}
Hal_uart_put_char('\n');
putstr("Hello World!\n");
Printf_test();
i = 100;
while(i--)
{
uint8_t ch = Hal_uart_get_char();
Hal_uart_put_char(ch);
}
}
static void Hw_init(void)
{
Hal_uart_init();
}
static void Printf_test(void)
{
char* str = "printf pointer test";
char* nullptr = 0;
uint32_t i = 5;
debug_printf("%s\b", "Hello printf");
debug_printf("output string pointer: %s\n", str);
debug_printf("%s is null pointer, %u number\n", nullptr, 10);
debug_printf("%u=5\n", i);
debug_printf("dec=%u hex=%x\n", 0xff, 0xff);
debug_printf("print zero %u\n", 0);
}
하드웨어 문제로 나머지, 나눗셈 연산 때문에 makefile 변경
LD를 gcc로 변경
LDFLAGS 표준 시작 파일, 표준 라이브러리, 디폴트 파일 등 사용 안함 추가 -lgcc는 잘모르겠다.
링킹 부분도 LDFLAGS 추가
ARCH=armv7-a
MCPU=cortex-a8
TARGET=rvpb
CC=arm-none-eabi-gcc
AS=arm-none-eabi-as
LD=arm-none-eabi-gcc
OC=arm-none-eabi-objcopy
LINKER_SCRIPT=./navilos.ld
MAP_FILE=build/navilos.map
ASM_SRCS=$(wildcard boot/*.S)
ASM_OBJS=$(patsubst boot/%.S, build/%.os, $(ASM_SRCS))
VPATH = boot \
hal/$(TARGET) \
lib
C_SRCS = $(notdir $(wildcard boot/*.c))
C_SRCS += $(notdir $(wildcard hal/$(TARGET)/*.c))
C_SRCS += $(notdir $(wildcard lib/*.c))
C_OBJS = $(patsubst %.c, build/%.o, $(C_SRCS))
INC_DIRS = -I include \
-I hal \
-I hal/$(TARGET) \
-I lib
CFLAGS = -c -g -std=c11
LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs -static -lgcc
navilos=build/navilos.axf
navilos_bin=build/navilos.bin
.PHONY: all clean run debug gdb
all:$(navilos)
clean:
@rm -fr build
run: $(navilos)
qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -nographic
debug: $(navilos)
qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -S -gdb tcp::1234,ipv4
gdb:
gdb-multiarch
$(navilos):$(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT)
$(LD) -n -T $(LINKER_SCRIPT) -o $(navilos) $(ASM_OBJS) $(C_OBJS) -Wl,-Map=$(MAP_FILE) $(LDFLAGS)
$(OC) -O binary $(navilos) $(navilos_bin)
build/%.os: %.S
mkdir -p $(shell dirname $@)
$(CC) -mcpu=$(MCPU) $(INC_DIRS) $(CFLAGS) -o $@ $<
build/%.o: %.c
mkdir -p $(shell dirname $@)
$(CC) -mcpu=$(MCPU) $(INC_DIRS) $(CFLAGS) -o $@ $<
빌드 진행하다보니
printf_buf가 없는데 책에 빠진부분을 깃헙 참고해서
lib/stdio.c 맨 위에 추가하고
NULL 타입 얘기도 나오길래 그냥 lib/stdio.c에 <stddef.h> 도 추가
수정한 lib/stdio.c 코드
#include <stdint.h>
#include <stddef.h>
#include "HalUart.h"
#include "stdio.h"
#define PRINTF_BUF_LEN 1024
static char printf_buf[PRINTF_BUF_LEN];
uint32_t putstr(const char* s)
{
uint32_t c =0;
while(*s)
{
Hal_uart_put_char(*s++);
c++;
}
return c;
}
uint32_t debug_printf(const char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(printf_buf, format, args);
va_end(args);
return putstr(printf_buf);
}
uint32_t vsprintf(char* buf, const char* format, va_list arg)
{
uint32_t c =0;
char ch;
char* str;
uint32_t uint;
uint32_t hex;
for(uint32_t i=0; format[i] ; i++)
{
if(format[i] == '%')
{
i++;
switch(format[i])
{
case 'c':
ch = (char)va_arg(arg,int32_t);
buf[c++] = ch;
break;
case 's':
str = (char*)va_arg(arg, char*);
if (str == NULL)
{
str = "(NULL)";
}
while(*str)
{
buf[c++] = (*str++);
}
break;
case 'u':
uint = (uint32_t)va_arg(arg, uint32_t);
c += utoa(&buf[c], uint, utoa_dec);
break;
case 'x':
hex = (uint32_t)va_arg(arg, uint32_t);
c += utoa(&buf[c], hex, utoa_hex);
break;
}
}
else
{
buf[c++] = format[i];
}
}
if (c >= PRINTF_BUF_LEN)
{
buf[0] = '\0';
return 0;
}
buf[c] = '\0';
return c;
}
uint32_t utoa(char* buf, uint32_t val, utoa_t base)
{
uint32_t c = 0;
int32_t idx = 0;
char tmp[11];
do
{
uint32_t t =val % (uint32_t)base;
if(t >= 10)
{
t += 'A' - '0' - 10;
}
tmp[idx] = (t + '0');
val /= base;
idx++;
}while(val);
//reverse
idx--;
while (idx >= 0)
{
buf[c++] = tmp[idx];
idx--;
}
return c;
}
빌드 하고 돌린 결과
테스트 코드가 잘 나왔다 !
소스트리 현황
'컴퓨터과학 > os' 카테고리의 다른 글
navilos - 10. UART 키보드 입력 인터럽트 (0) | 2022.08.21 |
---|---|
나빌로스 하다가 찾은 문제 (0) | 2022.08.20 |
navilos - 8. UART 2 putstr,getchar (0) | 2022.08.20 |
navilos - 7. UART 1 (0) | 2022.08.18 |
navilos - 6. 익셉션 핸들러 구현2, 메인함수 진입 (0) | 2022.08.18 |