ライブラリのすすめ
複数のプログラムで共通するサブルーチンは別のファイルに分けて書いておくと, 毎回書く必要がなくなり便利なだけでなく, サブルーチンに誤りが見つかったときに1箇所書き直すだけで全てのプログラムが修正できるなど, 利点は多い. 例えばディスプレイの1行を表示する関数は多くのプログラムで使うので, 別のファイルに切り分けておくと便利だろう.
例えば,メインルーチン(_start
から始まる)が入ったファイルをmain.s,
ディスプレイに関連するサブルーチンが入ったファイルをdisplay.sとする.
この2つのソースファイルから実行ファイルを作るには,次のようにすればよい.
- それぞれのソースファイルを個別にアセンブルしてオブジェクトファイルを作る.
$ arm-none-eabi-as main.s -o main.o $ arm-none-eabi-as display.s -o display.o
- 全てのオブジェクトファイルをリンクする.
$ arm-none-eabi-ld -m armelf main.o display.o -o main.elf
- イメージを作る.
$ arm-none-eabi-objcopy main.elf -O binary main.img
[重要] 実行開始位置
第III部の開発手順では,「imgファイルの先頭が実行開始位置」であることに注意する必要がある.
第I部・第II部と違って_start
ラベルは無関係であり,最終的に生成されるimgファイルの中で先頭に置かれた命令が,最初に実行される命令になる.
imgファイル中の配置は以下の規則に従う.この規則に合わせて,実行開始点がimgファイルの先頭になるよう,適切にソースファイルやオブジェクトファイルのリンク順を記述する必要がある.
プログラム中で
.section .init
と書くと,それ以降の機械語命令列は .init セクションの中に作られる. .init セクションは,.text や .data 等の他のセクションより前(低位番地)に配置される. .init セクション同士は以下の規則で並べられ,一つに結合される(.text 同士,.data 同士も同様).
- 同じファイルに複数 .initセクションがあるときは,ファイル中の記述順に並べる.
- 複数のファイルに .initセクションがあるときは,リンカのコマンドライン引数の順に並べる.
従って,実行開始点をimgファイルの先頭にするには,以下のどちらかを行えばよい.
- 実行開始ルーチンをファイルの先頭に記述した上で,そのファイルをリンカのコマンドライン引数の先頭に書く.または,
- 実行開始ルーチンのみ .init セクションに記述し,それ以外のサブルーチンは全て .text セクションに記述する(.init セクションを持つファイルは一つだけ).
「.initセクションは最初に実行されるプログラムを収めるセクション」という元々の意味を考えると,メインルーチン以外には .textセクションを使うのが(つまり2.の方が)良いだろう.
以上のようにして,メインルーチンから切り離してさまざまなプログラムで再利用できるように作った共通のルーチン集を, ライブラリと呼ぶ.
インクルードファイル
サブルーチンだけでなく,.equ
疑似命令を使った定数の定義を複数のソースファイルで共有したい場合がある.
そういうときは.include
疑似命令を使えばよい.
LEDのポート番号などの複数のソースファイルで共通する定義を,例えばcommon.hというファイルに書く. それを,各ソースファイルの先頭で次のようにして読み込む.
.include "common.h"
これによって,共通の定数定義を1つのファイルにまとめることができる.