Z80のC言語クロスコンパイル(SDCC)(5)

  • 投稿日:
  • 更新日:2015/03/08
  • by
  • カテゴリ:

前へ 第1回 第2回 第3回 第4回 第5回

演算ライブラリについて

sdccには基本的な標準ライブラリが入っています。 その中には

stdio.h
stddef.h
stdlib.h
string.h  

など、C言語として当たり前のものもあるのですが、その他に、基本的な演算や浮動小数点なども含まれます。
たとえば、整数の掛け算や割り算をする場合も、関連するライブラリがリンク時に埋め込まれます。z80は標準で掛け算や割り算の命令を持たないので、これは仕方のないところです。

ただ、演算ライブラリは関数単位ではなくオブジェクト単位で埋め込まれるので、不要なものまで入ってしまうことがあります。

演算ライブラリのリンク例

たとえば、次のような簡単なプログラムをコンパイルしてみましょう。

-- test.c
long mul(long a, long b) {
  return a * b;
}
void main () {
  mul(654321, 123456);
} 

今回のプログラムは試しやすいようにアーカイブを用意しました。

sdcc5.zip
(sdccとMakeのできる環境(UNIX系のOSやCygwinなど)が必要です)

Makefileが入っていますのでmakeのみでコンパイル可能です。 まずは、アーカイブのsample1ディレクトリでmakeしてみてください。

Makefileにはいろいろ書いてありますが、要は次のようにしているのと同じです。

% sdcc -mz80 --code-loc 0 --no-std-crt0 test.c  

今回は話を簡単にするため、crt0は使っていません。

さて、これでオブジェクトのてtest.ihxのほか、マッピングファイルのtest.mapが出力されていると思います。

mapファイルは、グローバルシンボルがどこに配置されているか、各エリアのサイズがどのくらいかが示されています。

そのmapファイルを見てみましょう。

-- test.map(抜粋)
(中略)
Area                               Addr   Size   Decimal Bytes (Attributes)
--------------------------------   ----   ----   ------- ----- ------------
_CODE                              0000   090D =   2317. bytes (REL,CON)

      Value  Global
   --------  --------------------------------
     0000    _mul
     0000    _mul_start
     0032    _main
     0032    _main_start
     0032    _mul_end
     004A    __mullong_rrf_s
     004A    __mullong_rrx_s
     004A    _main_end
     004D    __modslong_rrf_s
     004D    __modslong_rrx_s
     0050    __modulong_rrf_s
     0050    __modulong_rrx_s
     0053    __divslong_rrf_s
     0053    __divslong_rrx_s
     0056    __divulong_rrf_s
     0056    __divulong_rrx_s
     0059    __mulint_rrf_s
     005F    __divsint_rrf_s
     0065    __divuint_rrf_s
     006B    __mulschar_rrf_s
     0071    __divschar_rrf_s
     0077    __muluchar_rrf_s
     007D    __divuchar_rrf_s
     0083    __modschar_rrf_s
     0089    __moduchar_rrf_s
     008F    __modsint_rrf_s
     0095    __moduint_rrf_s
     009B    __rrulong_rrf_s
     00A1    __rrslong_rrf_s
     00A7    __rlulong_rrf_s
     00AD    __rlslong_rrf_s
     00B3    __divschar_rrx_s
     00BA    __divschar_rrx_hds
     00C1    __modschar_rrx_s
     00C8    __modschar_rrx_hds
     00CF    __divsint_rrx_s
     00DB    __divsint_rrx_hds
     00E3    __modsint_rrx_s
     00EF    __modsint_rrx_hds
     00F7    __divuchar_rrx_s
     00FE    __divuchar_rrx_hds
     0105    __moduchar_rrx_s
     010C    __moduchar_rrx_hds
     0113    __divuint_rrx_s
     011F    __divuint_rrx_hds
     0127    __moduint_rrx_s
     0133    __moduint_rrx_hds
     013B    .div8
     013B    .mod8
     0143    .div16
     0143    .mod16
     0180    .divu8
     0180    .modu8
     0183    .divu16
     0183    .modu16
     01B8    __rrulong_rrx_s
     01D5    __rrslong_rrx_s
     01F2    __rlslong_rrx_s
     01F2    __rlulong_rrx_s
     020F    __modslong
     020F    __modslong_start
     02E8    __modslong_end
     02E8    __modulong
     02E8    __modulong_start
     03FF    __divslong
     03FF    __divslong_start
     03FF    __modulong_end
     04E1    __divslong_end
     04E1    __muluchar_rrx_s
     04F4    __mulschar_rrx_s
     04FB    __mulschar_rrx_hds
     0507    __mulint_rrx_s
     0513    __mulint_rrx_hds
     0513    __muluchar_rrx_hds
     052C    __divulong
     052C    __divulong_start
     0610    __divulong_end
     0610    __mullong
     0610    __mullong_start
     090D    __mullong_end

いかがでしょうか。test.c本体は0x0000~0x0049に配置されています。staticスコープではない関数はstartとendのシンボルが付加されます。_main_endなどの終了位置は終了アドレス+1を示します。

そして、0x004a~0x090cはすべて演算用モジュールです。見ると、divだのmodだの、関係ないものまでたくさん入っていて、そのサイズは実に2,243バイトに達します。

小さなコードを生成するためには

では、もし標準ライブラリをまったくリンクしないとどうなるでしょう。

% sdcc -mz80 --code-loc 0 --no-std-crt0 --nostdlib test.c
?ASlink-Warning-Undefined Global '__mullong_rrx_s' referenced by module 'test'

こうなると思います。つまり、掛け算で'__mullong_rrx_s'という関数を呼び出しているわけです。

こ の関数が定義されているのは、 SDCC/lib/z80/stubs.o というオブジェクトなのですが、実はこのオブジェクトで演算の本体が実装されているわけではありません。名前のとおりこのオブジェクトはただのスタブで、 演算に関するありとあらゆるオブジェクトを参照するようになっています。

リンカはオブジェクト単位ですべての参照を解決しないと出力を生成しないため、スタブが参照しているオブジェクトをすべて埋め込む結果となってしまいます。

オブジェクトを追いかけてみる

ライブラリがリンクするオブジェクトを追いかけるためには、オブジェクトファイルの構造を知ることが必要ですが、ここでは最低限知っておけばいいことだけ挙げます。

先ほどのstubs.oを見ると、次のようになっています。

-- stubs.o (抜粋)
(中略)
S __muluchar_rrx_s Ref0000
S __mullong Ref0000
(中略)
S __rrulong_rrf_s Def0051
S __mullong_rrx_s Def0000
(中略)

最初の方にこのような"S"で始まる行がたくさんあります。これはシンボル定義で、シンボル名と参照・定義の種別を表していま す。Refとあるのは他のオブジェクトで定義されているシンボルの参照、Defとあるのがこのオブジェクトで定義されているシンボルと定義されているアド レスです。

リンカはリンクしようとしたオブジェクトにRefで示されるシンボルがあると、それに対応するDefのシンボルが定義されてい るオブジェクトをリンクします。stubs.oは大量のRefがあるため、それに対応するDefのあるオブジェクトをすべてリンクするまではリンクが終了 しません。

ソースを追いかけてみる

この問題を解決するためには、オブジェクトファイルを直接いじる手もあるのですが、幸いsdccにはソースがついているので、ソースを直接インポートしてしまうのが手っ取り早いでしょう。

ソースは、 SDCC/lib/src/z80/ に入っています。stubs.s を見ると、以下のようになっています。

-- stubs.s (抜粋)
__mullong_rrx_s::
__mullong_rrf_s::
        jp      __mullong

何のことはない、__mullongに飛んでいるだけです。__mullongは

SDCC/lib/src/__mullong.c

に入っています。

そこで、stubs.sをコピーして、必要なmullong_rrx_s以外の定義を削除してしまいます。次に__mullong.cを個別にコンパイルしてリンクします。すると次は、

?ASlink-Warning-Undefined Global '__muluchar_rrx_s' referenced by module '_mullong'

というリンクエラーが出ます。この定義は

SDCC/lib/src/z80/mul.s

に入っているもっとも基本のライブラリなので、これもアセンブルするようにします。

これらを済ませたものがアーカイブのsample2ディレクトリに入っています。

このディレクトリでmakeしてみてください。リンクエラーは出なくなったと思います。

結果生成されたmapファイルを見てみると、

-- test.map(抜粋)
Area                               Addr   Size   Decimal Bytes (Attributes)
--------------------------------   ----   ----   ------- ----- ------------
_CODE                              0000   0395 =    917. bytes (REL,CON)

      Value  Global
   --------  --------------------------------
     0000    _mul
     0000    _mul_start
     0032    _main
     0032    _main_start
     0032    _mul_end
     004A    __mullong
     004A    __mullong_start
     004A    _main_end
     0347    __mullong_end
     0347    __mullong_rrf_s
     0347    __mullong_rrx_s
     034A    __muluchar_rrx_s
     035D    __mulschar_rrx_s
     0364    __mulschar_rrx_hds
     0370    __mulint_rrx_s
     037C    __mulint_rrx_hds
     037C    __muluchar_rrx_hds

今度はリンクされる関数がかなり少なくなりました。リンクされたライブラリの大きさは818バイトです。

__mullong.cを最適化したり、mul.sで使われていないルーチンを削ったりすることでもっとサイズを削減することも可能です。

まぁ、そのような場合は計算用ライブラリを個別に用意して、それを呼び出した方がいいかもしれません。

ちなみに、SDOSでは、以下については標準ライブラリは使わず、アセンブリ言語で独自の実装を行っています。

  • 乗算(16ビット x 16ビット = 32ビット)
  • 除算(32ビット / 8ビット = 32ビット)

どちらが絶対的によいと言うわけではないのですが、16ビットまでの大部分の演算はライブラリを呼び出さずにインライン展開されるようなので、必要な演算の種類やメモリ・速度の制約などのトレードオフを勘案して使い分ければいいと思います。

前へ 第1回 第2回 第3回 第4回 第5回

こちらもよく読まれています