小さい printf、sprintf

sprintfだけのプログラム

以下のような短いプログラムを書きます。

#include <stdio.h>
 
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 ;
}

まとめるとこんな感じ。

printf.c
//*****************************************************************************
//      軽量版 putc,puts,printf,sprintf,vsprintf 関数
//                                                      (C)YdlProg
//*****************************************************************************
#include    <sysio.h>
#include    <stdio.h>
#include    <string.h>
#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
h8/小さい_printf_sprintf.txt · 最終更新: 2018/03/18 09:56 (外部編集)
 
特に明示されていない限り、本Wikiの内容は次のライセンスに従います: CC Attribution-Share Alike 4.0 International
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki