OSの起動に必要な「ブートローダー」を自作してみようH8マイコンボードで動作する組み込みOSを自作してみよう!(3)(2/3 ページ)

» 2011年06月14日 11時37分 公開
[坂井弘亮,@IT MONOist]
※本記事はアフィリエイトプログラムによる収益を得ています

 ブートローダーを書き込んだら、マイコンボードのディップスイッチを左から「ON、OFF、ON、OFF(上、下、上、下)」に設定します。PC側では「Tera Term」などの端末エミュレータ(注4)を「9600bps、データ長8ビット、ストップビット長1、パリティなし、フロー制御なし、XON/XOFFなし」で起動し、マイコンボードの電源を入れ直します。


※注4:UNIX系では、「kermit」や「minicom」などがあります。詳しくは連載第1回を参照してください。

 これでマイコンボードがフラッシュROMから起動し、先ほど書き込んだブートローダーが動作します。リスト3のように「kzload>」というプロンプトが出力され、指示待ちの状態になっていれば成功です。リスト3は「cu」というシンプルなシリアル接続用のソフトウェアで接続しているところです。

kzload (kozos boot loader) started.
kzload> 
リスト3 起動時の出力

 この状態で「load」と入力すると、ブートローダーは実行モジュールのダウンロード待ちに入ります。実行モジュールは「XMODEM」というプロトコルで、シリアル経由で転送できます。端末エミュレータのXMODEMファイル送信機能を利用することで、「Hello World」の実行モジュールである「kozos」をマイコンボード側に転送します。

 リスト4は「cu」と、さらに「lrzsz」というXMODEM通信用ソフトウェアを利用した場合の例です。cuでは「load」の実行後にキーボードから「~」「C」と入力し、さらに続けて「lsx <ファイル名>」と入力することで、指定したファイルをXMODEM転送できます。

kzload (kozos boot loader) started.
kzload> load
~CLocal command? lsx kozos
Sending kozos, 11 blocks: Give your local XMODEM receive command now.
Bytes Sent:   1536   BPS:165
 
Transfer complete
 
XMODEM receive succeeded.
kzload> 
リスト4 XMODEMによる実行モジュールの転送

 転送が完了した後、「run」と入力すると、実行モジュールに制御を渡します。リスト5で、「starting from……」と出力されているのはブートローダー(kzload)の出力、その後の「Hello World!」が、ブートローダーから起動された実行モジュール(kozos)の出力です。

kzload> run
starting from entry point: ffc020
Hello World!
> 
リスト5 転送したモジュールの起動

5.ブートローダーの仕組み

 ここで、KOZOSのブートローダーの動作について、簡単に説明します。

 ブートローダーのファイル構成を表1に示します。

ファイル名 行数 内容
defines.h 11 各種定義
ld.scr 60 リンカスクリプト
lib.c 135 ライブラリ
lib.h 18 「lib.c」のヘッダファイル
main.c 104 メイン関数
serial.c 112 シリアルドライバ
serial.h 10 「serial.c」のヘッダファイル
startup.s 10 スタートアップ(アセンブラ)
vector.c 18 割り込みベクターのテーブル
elf.c 94 ELF形式の解析
elf.h 6 「elf.c」のヘッダファイル
xmodem.c 94 XMODEMによるファイル転送
xmodem.h 6 「xmodem.c」のヘッダファイル
合計 678 全ソースコードの総ステップ数
表1 ブートローダーのソースコード一覧

 「ライブラリ」や「リンカスクリプト」などについては連載第2回で説明しました。今回は、新たに追加されている「xmodem.c」と「elf.c」というファイルについて説明します。

5.1.XMODEMによる通信

 まず、「xmodem.c」について説明しましょう。

 PCからマイコンボードへのファイル転送には、「XMODEM」というプロトコルを用います。これは、図2のようにブロック単位でデータを転送するプロトコルで、古くから使われているものです。仕様自体はレガシーで、転送効率が悪い、ファイルのサイズ情報を送れないなどの幾つかの欠点がありますが、とても“シンプル”な仕様のため、簡単に実装することができます。

XMODEMのブロックのフォーマット 図2 XMODEMのブロックのフォーマット 
※ファイル末尾でデータが128バイトに満たない場合は、「EOF(0x1a)」でパディングする

 実際に、ブートローダーのXMODEM処理部分を見てみましょう。リスト6は「xmodem.c」から、ブロック単位での受信処理部分を抜粋したものです。

/* ブロック単位での受信 */
static int xmodem_read_block(unsigned char block_number, char *buf)
{
  unsigned char c, block_num, check_sum;
  int i;
 
  block_num = serial_recv_byte(SERIAL_DEFAULT_DEVICE);
  if (block_num != block_number)
    return -1;
 
  block_num ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE);
  if (block_num != 0xff)
    return -1;
 
  check_sum = 0;
  for (i = 0; i < XMODEM_BLOCK_SIZE; i++) {
    c = serial_recv_byte(SERIAL_DEFAULT_DEVICE);
    *(buf++) = c;
    check_sum += c;
  }
 
  check_sum ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE);
  if (check_sum)
    return -1;
 
  return i;
}
リスト6 ブロック単位での受信部分

 「xmodem.c」では、SOHを受信するとリスト6の「xmodem_read_block()」が呼ばれます。「xmodem_read_block()」では、まず1バイトのブロック番号を受信し、さらに後続の1バイトを受信・ビット反転させ、ブロック番号に一致していることを確認します。続いて、128バイトのデータを受信し、最後に1バイトのチェックサムと比較してデータが壊れていないことを確認します。

 図2とリスト6を見比べてみてください。XMODEMのブロック構造は、非常に簡潔なため、ブロック受信部分は数十行で実装できています。さらに「xmodem.c」は、実はたったの94行です。「ファイル転送」というと難しく聞こえるかもしれませんが、このようにシンプルなプロトコルであるため、受信部分のみであればたった数百行で書けてしまうものなのです(注5)。

※注5:もちろんエラー処理などをきちんと入れれば数百行では済みませんが、ホビー用途ならばこれで十分といえます。

Copyright © ITmedia, Inc. All Rights Reserved.