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;
}

 

빌드 하고 돌린 결과

테스트 코드가 잘 나왔다 !

 

 

 

 

 

소스트리 현황

 

 

+ Recent posts