====== 小さい printf、sprintf ======
===== sprintfだけのプログラム =====
以下のような短いプログラムを書きます。
#include
void main(void) {
char buff[8] ;
sprintf(buff,"%c",'a') ;
printf("abc %s\n",buff) ;
}
マップファイルを見てみます。
Segment start end size ROM atr
main $200000 $200003 $4 $200000 CODE(R)
TEXT $200004 $203487 $3484 $200004 CODE(R)
DATA_CONST $203488 $2034A8 $21 $203488 IDATA(R)
DATA $2034AA $2035D9 $130 $2034AA IDATA(RW)
STACK $FFEF10 $FFF34F $440 $2035DA DATA(RW)
Symbol Address(hex) (ObjctFile,SegmentName)
__smr_adr $203488 (..\lib3052\startup.asm,DATA_CONST)
_STACK_BTM $FFEF50 (..\lib3052\startup.asm,STACK)
START $200004 (..\lib3052\startup.asm,TEXT)
__dbg_flag $2034A8 (..\lib3052\startup.asm,DATA_CONST)
__exit $20000E (..\lib3052\startup.asm,TEXT)
_main $20001A (test.ASM,TEXT)
__l_mul $200072 (CLIB.ASM,TEXT)
__l_divs $2000E0 (CLIB.ASM,TEXT)
__l_divu $2000B8 (CLIB.ASM,TEXT)
__l_mods $200158 (CLIB.ASM,TEXT)
__l_modu $200130 (CLIB.ASM,TEXT)
__d_dtol_set $200372 (FCLIB.ASM,TEXT)
__d_sub_set $2001F8 (FCLIB.ASM,TEXT)
__d_sltod_set $200352 (FCLIB.ASM,TEXT)
__d_neg_set $2001D4 (FCLIB.ASM,TEXT)
__d_cmp $200310 (FCLIB.ASM,TEXT)
__d_div $2002B4 (FCLIB.ASM,TEXT)
__d_mul $200258 (FCLIB.ASM,TEXT)
_printf $202EBE (PRINTF.ASM,TEXT)
_sprintf $202F1A (PRINTF.ASM,TEXT)
__Dnorm $202F86 (MATH.ASM,TEXT)
_fputc $203182 (STDIO.ASM,TEXT)
_fputs $20333E (STDIO.ASM,TEXT)
__iob $203510 (STDIO.ASM,DATA)
_strcat $2033EC (STRING.ASM,TEXT)
_strcpy $203398 (STRING.ASM,TEXT)
_strlen $203446 (STRING.ASM,TEXT)
これだけのプログラムでも内蔵RAMの容量を越えてしまいます。13KByteもあります。\\
内蔵RAMでは既にエミュレーションデバッグができません。\\
printfはYellow Scopeへのログ出力、sprintf はシリアルへのログ出力やLCDへの出力など\\
使う頻度は多いので、これでは困ります。
===== 秋月で売っているH8のROMとRAM =====
^ 型番 ^ ROM ^ RAM ^
| 3048F| 128KByte| 4KByte|
| 3052F| 512KByte| 8KByte|
| 3067| 128KByte| 4KByte|
| 3069F| 512KByte| 16KByte|
| 3664F| 32KByte| 2KByte|
| 3664N| 32KByte| 2KByte|
===== putc,putsと機能限定printf,sprintfの作成 =====
使う頻度が少ない(変わりの手段のある)%f、%eを省いて軽量化します。\\
使用できるのは%c,%s,%d,%u,%x,%X,と%-05d等の、左寄せ、桁数指定、0で埋める。\\
\\
まずは、Yellow Scopeへの出力を作ります。ライブラリのソースを見ながら作成。\\
ライブラリの処理の仕方に疑問が残りますが…まぁいいでしょう。
extern unsigned char *_smr_adr[] ; // 標準入出力関数の入出力先の割り当て
//*****************************************************************************
// Yellow Scope へ1文字出力
//
// _putc_ys
// 2004/12/23 : 初期バージョン完成
//
// [IN] char code = 出力コード
//
// [OUT] なし
//*****************************************************************************
static void _putc_ys(char c) {
Uint8 *tdr = _smr_adr[1] + 3 ;
Uint8 *ssr = _smr_adr[1] + 4 ;
while((*ssr & 0x80) == 0) ; // 送信完了待ち
*tdr = c ; // 送信
*ssr &= ~0x80 ; // フラグクリア
}
次にputcを作ってしまいます。putsはputcを文字数分呼ぶだけですし。\\
簡単なプロトコルがありますので、これもライブラリのソースを見ながら作ります。\\
//*****************************************************************************
// 標準出力に1文字出力
//
// _putc
// 2004/12/23 : 初期バージョン完成
//
// [IN] char code = 出力コード
//
// [OUT] なし
//*****************************************************************************
void _putc(char code) {
Uint8 *scr = _smr_adr[1] + 2 ;
*scr &= ~0x40 ;
_putc_ys(0x48) ;
_putc_ys(0x01) ;
_putc_ys(code) ;
_putc_ys(-(0x48 + 0x01 + code)) ;
*scr |= 0x40 ;
}
putsは単純ですね。
//*****************************************************************************
// 標準出力に文字列出力
//
// _puts
// 2004/12/23 : 初期バージョン完成
//
// [IN] char *str = 出力文字列
//
// [OUT] なし
//*****************************************************************************
void _puts(const char *str) {
while(*str) {
_putc(*str) ;
*str ++ ;
}
}
printfとsprintf。printfは256Byteのバッファに、一旦sprintfしてputsを呼び出します。\\
バッファオーバーとかは気にしない方向で。書式文字列の展開は同じ処理を呼び出してます。
//*****************************************************************************
// 軽量版 printf
//
// _printf
// 2004/12/22 : 初期バージョン完成
//
// [IN] char *format = 書式文字列
//
// [OUT] なし
//*****************************************************************************
void _printf(const char *format,...) {
char buff[256] ;
va_list argptr ;
va_start(argptr,format) ;
_vsprintf(buff,format,argptr) ;
va_end(argptr) ;
_puts(buff) ;
}
//*****************************************************************************
// 軽量版 sprintf
//
// _sprintf
// 2004/12/22 : 初期バージョン完成
//
// [IN] char *buff = 展開バッファのポインタ
// char *format = 書式文字列
//
// [OUT] なし
//*****************************************************************************
void _sprintf(char *buff,const char *format,...) {
va_list argptr ;
va_start(argptr,format) ;
_vsprintf(buff,format,argptr) ;
va_end(argptr) ;
}
最後に書式文字列の解釈ルーチン。思いつくまま記述したので、もう少し小さくできる気がします。
//*****************************************************************************
// 軽量版 _vsprintf
//
// _vsprintf
// 2004/12/23 : 初期バージョン完成
//
// [IN] char *buff = 展開バッファのポインタ
// char *format = 書式文字列
// va_list argptr = 引数リストのポインタ
//
// [OUT] なし
//
// 備考:%c %s %d %u %x %X と %-05d 等の、左寄せ、桁数指定、
// 0 で埋めるは使用可能。%f %e 等の小数点を含むものは使用できない。
//*****************************************************************************
void _vsprintf(char *buff,const char *format,va_list argptr) {
Uint8 lflag ;
int ecx,len ;
while(*format) {
switch(*format) {
case '%' :
format ++ ;
lflag = 0 ;
if(*format == '-') { // 左詰め
lflag |= (1 << 0) ;
format ++ ;
}
if(*format == '0') { // 0 で埋める
lflag |= (1 << 1) ;
format ++ ;
}
len = 0 ; // 桁数指定
if((*format >= '1') && (*format <= '9')) {
while((*format >= '0') && (*format <= '9')) {
len = len * 10 + *format - '0' ;
format ++ ;
}
}
switch(*format) {
case 'c' :
if(len) {
memset(buff,' ',len) ;
if(lflag & (1 << 0)) {
*buff = va_arg(argptr,char) ;
}
else {
buff[len - 1] = va_arg(argptr,char) ;
}
buff += len ;
}
else {
*buff = va_arg(argptr,char) ;
buff ++ ;
if(*buff) buff ++ ; // パッチ 2005/01/14
}
format ++ ;
break ;
case 's' : {
char *str = va_arg(argptr,char *) ;
int slen = strlen(str) ;
if(len) {
if(slen > len) len = slen ;
memset(buff,' ',len) ;
if(lflag & (1 << 0)) {
memcpy(buff,str,slen) ;
}
else {
memcpy(&buff[len - slen],str,slen) ;
}
buff += len ;
}
else {
memcpy(buff,str,slen) ;
buff += slen ;
}
format ++ ;
break ;
}
case 'd' :
case 'u' :
case 'x' :
case 'X' : {
char str[6] ;
int slen = 0 ;
BOOL act = FALSE ;
switch(*format) {
case 'd' : {
int num = va_arg(argptr,int) ;
if(num < 0) {
*buff = '-' ;
buff ++ ;
if(len) len -- ;
num = ~num + 1 ;
}
for(ecx = 10000 ; ecx != 0 ; ecx /= 10) {
str[slen] = num / ecx + '0' ;
num %= ecx ;
if((str[slen] != '0') || (act)) {
act = TRUE ;
slen ++ ;
}
}
break ;
}
case 'u' : {
Uint16 num = va_arg(argptr,Uint16) ;
for(ecx = 10000 ; ecx != 0 ; ecx /= 10) {
str[slen] = num / ecx + '0' ;
num %= ecx ;
if((str[slen] != '0') || (act)) {
act = TRUE ;
slen ++ ;
}
}
break ;
}
case 'x' :
case 'X' : {
Uint16 num = (Uint16)va_arg(argptr,int) ;
for(ecx = 0 ; ecx < 16 / 4 ; ecx ++) {
str[slen] = (num >> (12 - (ecx << 2))) & 0x0f ;
if(str[slen] <= 9) str[slen] += '0' ;
else if(*format == 'x') str[slen] += 'a' - 10 ;
else str[slen] += 'A' - 10 ;
if(str[0] == '0') continue ;
slen ++ ;
}
break ;
}
}
if(!slen) slen ++ ;
if(len) {
if(slen > len) len = slen ;
if(lflag & (1 << 1)) memset(buff,'0',len) ;
else memset(buff,' ',len) ;
if(lflag & (1 << 0)) {
memcpy(buff,str,slen) ;
}
else {
memcpy(&buff[len - slen],str,slen) ;
}
buff += len ;
}
else {
memcpy(buff,str,slen) ;
buff += slen ;
}
format ++ ;
break ;
}
}
break ;
default :
*buff = *format ++ ;
buff ++ ;
break ;
}
}
*buff = 0 ;
}
まとめるとこんな感じ。
//*****************************************************************************
// 軽量版 putc,puts,printf,sprintf,vsprintf 関数
// (C)YdlProg
//*****************************************************************************
#include
#include
#include
#include "../lib3052/typedef.h"
//=============================================================================
// プロトタイプ宣言
//=============================================================================
//-------------------------------------------------------------------------
// sprintf.c
//-------------------------------------------------------------------------
extern unsigned char *_smr_adr[] ; // 標準入出力関数の入出力先の割り当て
//*****************************************************************************
// Yellow Scope へ1文字出力
//
// _putc_ys
// 2004/12/23 : 初期バージョン完成
//
// [IN] char code = 出力コード
//
// [OUT] なし
//*****************************************************************************
static void _putc_ys(char c) {
Uint8 *tdr = _smr_adr[1] + 3 ;
Uint8 *ssr = _smr_adr[1] + 4 ;
while((*ssr & 0x80) == 0) ; // 送信完了待ち
*tdr = c ; // 送信
*ssr &= ~0x80 ; // フラグクリア
}
//*****************************************************************************
// 標準出力に1文字出力
//
// _putc
// 2004/12/23 : 初期バージョン完成
//
// [IN] char code = 出力コード
//
// [OUT] なし
//*****************************************************************************
void _putc(char code) {
Uint8 *scr = _smr_adr[1] + 2 ;
*scr &= ~0x40 ;
_putc_ys(0x48) ;
_putc_ys(0x01) ;
_putc_ys(code) ;
_putc_ys(-(0x48 + 0x01 + code)) ;
*scr |= 0x40 ;
}
//*****************************************************************************
// 標準出力に文字列出力
//
// _puts
// 2004/12/23 : 初期バージョン完成
//
// [IN] char *str = 出力文字列
//
// [OUT] なし
//*****************************************************************************
void _puts(const char *str) {
while(*str) {
_putc(*str) ;
*str ++ ;
}
}
//*****************************************************************************
// 軽量版 printf
//
// _printf
// 2004/12/22 : 初期バージョン完成
//
// [IN] char *format = 書式文字列
//
// [OUT] なし
//*****************************************************************************
void _printf(const char *format,...) {
char buff[256] ;
char *str = buff ;
va_list argptr ;
va_start(argptr,format) ;
_vsprintf(buff,format,argptr) ;
va_end(argptr) ;
_puts(str) ;
}
//*****************************************************************************
// 軽量版 sprintf
//
// _sprintf
// 2004/12/22 : 初期バージョン完成
//
// [IN] char *buff = 展開バッファのポインタ
// char *format = 書式文字列
//
// [OUT] なし
//*****************************************************************************
void _sprintf(char *buff,const char *format,...) {
va_list argptr ;
va_start(argptr,format) ;
_vsprintf(buff,format,argptr) ;
va_end(argptr) ;
}
//*****************************************************************************
// printf,sprintf 展開処理
//
// _vsprintf
// 2004/12/23 : 初期バージョン完成
//
// [IN] char *buff = 展開バッファのポインタ
// char *format = 書式文字列
// va_list argptr = 引数リストのポインタ
//
// [OUT] なし
//
// 備考:%c %s %d %u %x %X と %-05d 等の、左寄せ、桁数指定、
// 0 で埋めるは使用可能。%f %e 等の小数点を含むものは使用できない。
//*****************************************************************************
void _vsprintf(char *buff,const char *format,va_list argptr) {
Uint8 lflag ;
int ecx,len ;
while(*format) {
switch(*format) {
case '%' :
format ++ ;
lflag = 0 ;
if(*format == '-') { // 左詰め
lflag |= (1 << 0) ;
format ++ ;
}
if(*format == '0') { // 0 で埋める
lflag |= (1 << 1) ;
format ++ ;
}
len = 0 ; // 桁数指定
if((*format >= '1') && (*format <= '9')) {
while((*format >= '0') && (*format <= '9')) {
len = len * 10 + *format - '0' ;
format ++ ;
}
}
switch(*format) {
case 'c' :
if(len) {
memset(buff,' ',len) ;
if(lflag & (1 << 0)) {
*buff = va_arg(argptr,char) ;
}
else {
buff[len - 1] = va_arg(argptr,char) ;
}
buff += len ;
}
else {
*buff = va_arg(argptr,char) ;
buff ++ ;
if(*buff) buff ++ ; // パッチ 2005/01/14
}
format ++ ;
break ;
case 's' : {
char *str = va_arg(argptr,char *) ;
int slen = strlen(str) ;
if(len) {
if(slen > len) len = slen ;
memset(buff,' ',len) ;
if(lflag & (1 << 0)) {
memcpy(buff,str,slen) ;
}
else {
memcpy(&buff[len - slen],str,slen) ;
}
buff += len ;
}
else {
memcpy(buff,str,slen) ;
buff += slen ;
}
format ++ ;
break ;
}
case 'd' :
case 'u' :
case 'x' :
case 'X' : {
char str[6] ;
int slen = 0 ;
BOOL act = FALSE ;
switch(*format) {
case 'd' : {
int num = va_arg(argptr,int) ;
if(num < 0) {
*buff = '-' ;
buff ++ ;
if(len) len -- ;
num = ~num + 1 ;
}
for(ecx = 10000 ; ecx != 0 ; ecx /= 10) {
str[slen] = num / ecx + '0' ;
num %= ecx ;
if((str[slen] != '0') || (act)) {
act = TRUE ;
slen ++ ;
}
}
break ;
}
case 'u' : {
Uint16 num = va_arg(argptr,Uint16) ;
for(ecx = 10000 ; ecx != 0 ; ecx /= 10) {
str[slen] = num / ecx + '0' ;
num %= ecx ;
if((str[slen] != '0') || (act)) {
act = TRUE ;
slen ++ ;
}
}
break ;
}
case 'x' :
case 'X' : {
Uint16 num = (Uint16)va_arg(argptr,int) ;
for(ecx = 0 ; ecx < 16 / 4 ; ecx ++) {
str[slen] = (num >> (12 - (ecx << 2))) & 0x0f ;
if(str[slen] <= 9) str[slen] += '0' ;
else if(*format == 'x') str[slen] += 'a' - 10 ;
else str[slen] += 'A' - 10 ;
if(str[0] == '0') continue ;
slen ++ ;
}
break ;
}
}
if(!slen) slen ++ ;
if(len) {
if(slen > len) len = slen ;
if(lflag & (1 << 1)) memset(buff,'0',len) ;
else memset(buff,' ',len) ;
if(lflag & (1 << 0)) {
memcpy(buff,str,slen) ;
}
else {
memcpy(&buff[len - slen],str,slen) ;
}
buff += len ;
}
else {
memcpy(buff,str,slen) ;
buff += slen ;
}
format ++ ;
break ;
}
}
break ;
default :
*buff = *format ++ ;
buff ++ ;
break ;
}
}
*buff = 0 ;
}
===== 結果と使い方 =====
まずは小さくなったか、マップファイルを見てみます。
Segment start end size ROM atr
main $200000 $200003 $4 $200000 CODE(R)
TEXT $200004 $200B3F $B3C $200004 CODE(R)
DATA_CONST $200B40 $200B5F $20 $200B40 IDATA(R)
DATA $200B60 $200B6C $D $200B60 IDATA(RW)
STACK $FFEF10 $FFF34F $440 $200B6E DATA(RW)
Symbol Address(hex) (ObjctFile,SegmentName)
__smr_adr $200B40 (..\lib3052\startup.asm,DATA_CONST)
_STACK_BTM $FFEF50 (..\lib3052\startup.asm,STACK)
START $200004 (..\lib3052\startup.asm,TEXT)
__exit $20000E (..\lib3052\startup.asm,TEXT)
_main $20001A (test.ASM,TEXT)
__putc $2000DA (..\lib3052\printf.ASM,TEXT)
__puts $200148 (..\lib3052\printf.ASM,TEXT)
__sprintf $2001DA (..\lib3052\printf.ASM,TEXT)
__printf $200182 (..\lib3052\printf.ASM,TEXT)
__vsprintf $20021A (..\lib3052\printf.ASM,TEXT)
__w_shift_ur $200A28 (CLIB.ASM,TEXT)
_memcpy $200A8C (STRING.ASM,TEXT)
_memset $200AEE (STRING.ASM,TEXT)
_strlen $200A4A (STRING.ASM,TEXT)
2.8Kぐらいになりました。アセンブラ化すればもっと減るかもしれません。\\
内蔵8KByteの3052Fでも、小さいプログラムならエミュレーションデバッグできます。\\
\\
軽量版関数を使用する場合は、以下のように定義すると便利です。
#undef putc // YC で定義済みのものを消す
#define putc _putc
#define puts _puts
#define printf _printf
#define sprintf _sprintf
#define vsprintf _vsprintf