平成26年4月24日
C言語の開始処理(続き2)
C言語のスタートアップ処理に関して必要な処理内容の説明が終わりました。次は具体的な実装方法について説明します。
C言語の開始処理で説明したように、スタートアップ処理では以下の処理を行います。
1.スタックポインタに初期値を設定する
2.割り込み処理アドレスに割り込み処理関数へのジャンプ命令を配置する。
3.初期値の無い(初期値がゼロである)変数の変数領域をゼロにクリアする。
(4.関数main()へジャンプする)
ここでの疑問は、下記になります。
1.に関して、スタックポインタの初期値には何をセットすればいいか?
2.に関しては、モニタプログラムの移植で説明したようにRAM領域へのジャンプ命令を設定しますので疑問はありません。
3.に関しては、初期値の無い変数のアドレスは具体的には何処から何処までか?
これらの疑問に対する答えはMAPファイルにあります。Hitech Cでは、Cコンパイラが管理する4つのグループのアドレス範囲をそれぞれ”__L<グループ名>”、”__H<グループ名>”というラベル名で定義してくれます。このアドレス情報を基にしてスタートアップ処理ルーチンを記述します。
ROM化を指定した場合には、上記のラベルに加えてdataグループの初期値アドレスの先頭を示す”__Bdata”が定義されます(終わりを示すラベルは定義されません)。メモリマップを図にすると次のようになります。

図(a). デバッグ時のメモリマップ 図(b). ROM化時のメモリマップ
SPへの設定に関して
ここでスタックポインタの初期値について、少し書いておきます。Cコンパイラの標準仕様では、スタックに必要なメモリ量をコンパイラに示すことでラベル__Hstackの値を計算させSPに設定するのが本来の使い方です。しかし組み込み用途ではヒープメモリを使用しないことも多く、この場合は製品に搭載されているRAMの上限アドレスをSPに設定することで、変数領域を除く残りのRAM全てをスタック領域として使用する方が合理的です。
この方法の欠点は、製品が変わるとRAMの上限アドレスも変わってしまうので、製品ごとにスタートアップルーチンを書き変える必要が出てきます。
いずれにしても、Hitech Cの使用するアセンブラでは、スタックの使用量を変更するだけでもスタートアップルーチンの修正が必要になります。これは外部から動的にラベルや定数を定義できない(コマンドラインからの定義が出来ない)ので、スタートアップルーチンを汎用的にライブラリ化するのは諦めた方が良いでしょう。無理にしようとすると変数の依存関係が複数のファイルにまたがるのでかえって分かりにくくなります。
(平成26年4月27日追記)
リンクの段階でSPへの初期値を渡せないかと考えていたら、方法がありましたので内容を変更します。リンカでは各グループの開始アドレスを指定することが出来ます。これに対してSPにはスタック領域の上限アドレス(この場合は終了アドレスに相当)を設定する必要があります。このためスタック領域に見かけ上何も配置しなければ、開始アドレス=終了アドレスとなるので、リンカを介してSPの初期値を設定できます。
(追記ここまで)
これでスタートアッププログラムを書くことが出来ます。以下にデバッグ用のstart.asと製品ROM化用のstart_r.asの主要部分を示します。
デバッグ用のstart.asリスト(主要部分のみ)
; Hitech-c for z80(CP/M) startup(CRT)
;
psect text,global,pure
psect data,global
psect bss,global
psect stack,global
; defs 256 ;SP値はリンカで設定する
psect text
__startup:
JP __start
JP RST8
JP RST10
JP RST18
JP RST20
JP RST28
JP RST30
JP RST38
JP NMI
;
__start:
global _main, __Hstack, __Lbss, __Hbss
ld sp, __Hstack
;
; 初期値のあるデータをROMからRAMへ転送する
;
; LD HL, __Hdata
; LD DE, __Ldata
; XOR A ;carry clear
; SBC HL, DE
; PUSH HL ;cnt
; POP BC
; LD HL, __Bdata ;src
; LDIR
;
; 初期値のないRAMエリアを0クリアする
;
LD IX, __Lbss
LD HL, __Hbss ;
LD DE, __Lbss
XOR A ;carry clear
SBC HL,DE ;BSS size
LD DE,1
L1:
SBC HL,DE
JP C,L2
LD (IX),0
INC IX
JP L1
L2:
call _main
jp __startup
製品ROM化用のstart_r.asリスト(主要部分のみ)
; Hitech-c for z80(CP/M) startup(CRT)
;
psect vec,local,abs,pure
psect text,global,pure
psect data,global
psect bss,global
psect stack,global
; defs 256 ;SP値はリンカで設定する
psect vec
ORG 0
__startup:
JP __start
ORG 08H
JP RST8
ORG 10H
JP RST10
ORG 18H
JP RST18
ORG 20H
JP RST20
ORG 28H
JP RST28
ORG 30H
JP RST30
ORG 38H
JP RST38
ORG 66H
JP NMI
;
psect text
__start:
global _main, __Hstack, __Hdata, __Ldata, __Bdata, __Lbss, __Hbss
ld sp, __Hstack
;
; 初期値のあるデータをROMからRAMへ転送する
;
LD HL, __Hdata
LD DE, __Ldata
XOR A ;carry clear
SBC HL, DE
PUSH HL ;cnt
POP BC
LD HL, __Bdata ;src
LDIR
;
; 初期値のないRAMエリアを0クリアする
;
LD IX, __Lbss
LD HL, __Hbss ;
LD DE, __Lbss
XOR A ;carry clear
SBC HL,DE ;BSS size
LD DE,1
L1:
SBC HL,DE
JP C,L2
LD (IX),0
INC IX
JP L1
L2:
call _main
jp __startup