TMPZ84C015上のモニターでSDCCによるc言語でprintfを実行してみました。

以前作成しているモニターではTMPZ84C015のシリアルの初期化を行っているので、まずはprintf文に必要なputchar()を作成してみます。

使用したSDCCは下記のバージョンのものです。

$ sudo apt-get install sdcc

$ sdcc -v
SDCC : mcs51/z80/z180/r2k/r3ka/gbz80/tlcs90/ds390/TININative/ds400/hc08/s08/stm8 3.8.0 #10562 (Linux)
published under GNU General Public License (GPL)

SDCCでは、main文を呼び出すための初期化と後始末を行うcrt0.sがありますが、今回の仕様になるように変更しました。

変更点は、スタックは設定せず、モニターに任せます。

帰りは、retでモニターに戻ります。

変更したcrt0.sは次の通りです。 mycrt0.asmで保存します。

	.module crt0
	.globl	_main


	;; Ordering of segments for the linker.

	.area	_CODE


	.area	_DATA


	.area   _CODE
init::

	call	_main
	ret


	.area	_DATA

作成したmycrt0.asmをアセンブルします。

$ asz80 -l -s -o mycrt0.asm

cのコンパイルには作成された mycrt0.relを使用します。

次にputcharを作成します。

putcharの本体は、モニターでも使用している一文字出力ルーチンです。

mainルーチンから呼ばれると、まず、引数をスタックにセットし、続いてputcharからの戻りアドレスをスタックに積みます。

putcharのルーチン内は、引数を取り出すために、戻りアドレスを+2したスタック値で引数を取得します。
返り値は、HLをクリアしています。
putchar.asmで保存します。

;;   int putchar(uint8_t)
;;
_putchar::
	ld	hl,	#0002
	add	hl,	sp
putchar01:
	in	a,	(0x1b)
	bit	2,	a
	jr	z,	putchar01
	ld	a,	(hl)
	out	(0x1a),	a
	ld	hl,	#0000
	ret

アセンブルします。

$ asz80 -l -s -o putchar.asm

putcharを使用した例題を作成ます。

mainでputchar()を呼び出し、”J”を表示します。
putchar_main.cで保存します。

#include	<stdint.h>

extern	uint8_t	 putchar(uint8_t);

int main(void)
{
	putchar(0x4a);
	return(0);
}

コンパイルします。
先ほど作成したmycrt0.relとputchar.relが必要になります。

$ sdcc -mz80 --out-fmt-ihx --code-loc 0xa000 --no-std-crt0 -o putchar_main.ihx mycrt0.rel putchar_main.c putchar.rel
Conflicting flags in area    _CODE
Conflicting flags in area    _DATA
Conflicting flags in area    _CODE
Conflicting flags in area    _DATA

作成されたputchar_main.ihxをモニターで読み込み実行してみました。

>c a000 y:
J
>

putcharが正常に動くようですので、purintfを実行してみます。

下記の例題を用意します。
printf_main.cで保存します。

#include	<stdio.h>

int main(void)
{
	printf("Hello SDCC Z80 C world\n");
	return(0);
}

コンパイルします。
先ほど作成したmycrt0.relとputchar.relが必要になります。

$ sdcc -mz80 --out-fmt-ihx --code-loc 0xa000 --no-std-crt0 -o printf_main.ihx mycrt0.rel printf_main.c putchar.rel
Conflicting flags in area    _CODE
Conflicting flags in area    _DATA
Conflicting flags in area    _CODE
Conflicting flags in area    _DATA

作成されたprintf_main.ihxをモニターで読み込み実行してみました。

>c a000 y:
Hello SDCC Z80 C world

>

printf上で算術演算を行ってみます。
この場合には算術演算等の処理のために容量がすごく多くなります。
printf_purasu.cで保存します。

#include	<stdio.h>

int main(void)
{
	printf("10 + 25=%d\n",10 + 25);
	return(0);
}

コンパイルします。
先ほど作成したmycrt0.relとputchar.relが必要になります。

$ sdcc -mz80 --out-fmt-ihx --code-loc 0xa000 --no-std-crt0 -o printf_purasu.ihx mycrt0.rel printf_purasu.c putchar.rel
Conflicting flags in area    _CODE
Conflicting flags in area    _DATA
Conflicting flags in area    _CODE
Conflicting flags in area    _DATA

作成されたprintf_purasu.ihxをモニターで読み込み実行してみました 。

>c a000 y:
10 + 25=35
>

SDCCで色々できそうなので、Cプログラムに挑戦していきたいと思います。

コメント一覧
  1. 匿名 より:

    penginさん、はじめまして、
    SDCCを用いた Cプログラムのモニタ起動、興味をもちました。
    もし、ご存知でしたら教えてください。sinf()やcosf()など
    浮動小数点の動作を試みたのですが上手くいかないです。
    SDCC で実現できそうでしょうか?
    こちらではz88dk (zcc) で現在、チャレンジ中です。
    進展ありましたらまた投稿したいとおもいます。

    • pengin より:

      こんにちは。
      サイトを訪問いただきありがとうございます。
      sinf(),conf()などの浮動小数点について、ちょっと確認してみました。
      printfで"%f"で表示すると”NO FLOAT”と表示されて表示か正常にできないことを確認しました。
      こちらのmsx-sdcc@ウィキさんのページ(https://w.atwiki.jp/msx-sdcc/)の”floatについて”で確認しましたところ、
      同じような現象のようです。
      printf_large.cには"%f"の書式らしきものが記載されているとの情報なので、z80.libにライブライリされているか
      確認しました。バイナリエディタでz80.libを覗くと、printf_large.relの記載が読めました。
      また、printf_large.cのdefine部分で USE_FLOATS 1 の表記があります。
      SDCCのリファレンスを覗くとコンパイルオプション(3.14.2のprintf()の項)で -DUSE_FLOATS=1を記載したらよいとの情報が
      あったので、試してみましたが、浮動小数点の表示にはなりませんでした。
      ちょっと残念な結果になりましたが、ご参考になれば幸いです。
      コメントいただきありがとうございました。

  2. 匿名 より:

    penginさん、こちらこそコメントくださりありがとうございます。
    ・z88dk(zcc)では、ihxファイル作成のための開始アドレス設定などの方法がみつからず断念しました、汗)。
    ・そこで、SDCC にもどって、所望の浮動小数点計算は、(int)x * 1000; などで欲しい桁数を整数に入れ込むことで、所望の計算ができるように解決(妥協?)しました、汗)。
    ・SDCCを使用した浮動小数点計算のポイントは、コンパイルのときに、--data-loc 0xa000 と、SRAM領域を指定しないと、code領域を上書きしてしまいうまく動作しないことです。
    ・下記は、アスキーアート計算のSDCC版です。
     ihxファイルは、
    >sdcc -mz80 -DUSE_FLOAT=1 --out-fmt-ihx --code-loc 0x8000 --data-loc 0xa000 --no-std-crt0 -o mandelbrot.ihx mycrt0.rel mandelbrot.c putchar.rel
    で生成します。

    #include
    #include

    int main(void)
    {
    int x, y, i;
    float ca, cb, a, b, t;

    for (y=-12; y<=12; y++) {
    for (x=-39; x<=39; x++) {
    ca = (float)x * 0.0458;
    cb = (float)y * 0.08333;
    a = ca;
    b = cb;
    for (i=0; i4) {
    if (i>9) {
    i = i + 7;
    }
    printf("%c", 48+i);
    break;
    }
    }
    if (i==16) {
    printf(" ");
    }
    }
    printf("\n\r");
    }
    return 0;
    }

    以上です。Grant's BASIC(NASCOM BASIC, Microsoft BASIC 4.7b)版の計算速度と
    引けを取らないくらい遅いです、汗)

    • pengin より:

      こんにちは。
      早速のコメントいただきありがとうございます。
      コメントの中のプログラムがTABの消失と<>内で見えなくなってしまいました^^すみません(^^;)
      floatの計算時は、--code-loc 0x8000 --data-loc 0xa000(RAM上)のようにコードとデータを配置することが必要ということですね。
      -DUSE_FLOAT=1 の設定は、printf("%f",abc);のようにfloat値を出力するときに必要で、たぶんZ80では機能しないと思われます。
      匿名さんのアイデアの”(int)x * 1000; などで欲しい桁数を整数に入れ込むことで、所望の計算ができるように解決”を参考に、printfで
      小数点を出力するためのprint_float(float,int)を作成してみました。
      print_float(float,int)では、定数点以下の必要桁数をpowf(10.0,(float)b)で作成し、その必要桁分を出力したい数値に掛けます。
      掛けた数値の除算と余りで、整数(除算).小数(余り)で表示しています。
      円の面積(r*r*PI)を求めてみました。r=2.5としています。
      TABが消失して読みにくくなりました(^^;)

      // Find the area of ​​a circle
      //
      //$ sdcc -mz80 --out-fmt-ihx --code-loc 0x8000 --data-loc 0xa000 --no-std-crt0 -o pi_main.ihx mycrt0.rel pi_main.c putchar.rel
      //
      //

      #include <stdio.h>
      #include <math.h>
      #include <float.h>

      void print_float(float,int);

      int main(void){

      float r,s;

      r = 2.5;
      s = r*r*PI;

      print_float(r,1);
      printf("*");
      print_float(r,1);
      printf("*");
      print_float(PI,2);
      printf("=");
      print_float(s,3);
      printf("\n");
      return(0);
      }
      void print_float(float a,int b)
      {
      int f,fa,ff,fs;
      f=powf(10.0,(float)b);
      fa=a*f;
      ff=fa/f;
      fs=fa%f;
      printf("%d.%d",ff,fs);
      }

      実行結果
      >c 8000 y:
      2.5*2.5*3.14=19.634

      >

      • 匿名 より:

        penginさん、コメントくださりありがとうございます。
        print_float()、こちらでも再現できました。
        -DUSE_FLOAT=1 の設定は不要ですね。
        これで計算機らしくmath.hライブラリも使えます。
        小数点位置付き関数、便利に使わせていただきます。
        動作も含めていろいろとレトロCPUを
        愉しめて嬉しい限りです、はい。
        ご対応くださりありがとうございました。

        • pengin より:

          こんにちは。
          正常に動作してよかったです。
          これからもレトロCPUに萌えていきたいと思います。
          コメントいただきありがとうございました。

SDCC, Z80の関連記事
おすすめの記事