(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

今回は,いよいよブートローダーでプログラムをロードして,実行してみよう.最終的にやりたいのは,ブートローダーでの起動後にOSをロードして実行することでOSに処理を渡すことなのだけど,とりあえずはブートローダーで起動して,簡単なコマンド受け付けのサンプルプログラムをロードして実行してみる.

まずロードするプログラムのオブジェクトフォーマットなのだけど,いろいろ考えたのだけどELF形式で行くことにした.というのは,まあぼくがELFの記事を書いたことがあって馴染み深くてよく知っているのと,ELFでできないことはそうそう無いので融通が効くこと,フォーマット解析がそんなに難しくないこと(ローダの実装がラクそう),標準的なフォーマットとして普通に使われていること,などが理由だ.ELFフォーマットについて詳細は,この記事の第2回をぜひ読んでほしい.かなり詳細に書いてある.(追記:この記事を書いている現段階では,モトローラSフォーマットを知った今となってはそっちにすればよかったかなと思います.展開もラクそうだし,あとロード先に直接展開ができるのでRAMを最大限まで使えるし.まあ対応は簡単そうなので,そのうち対応しよう)

で,ELFの実行形式をいったんバッファにロードしてから実行コマンドでロード(メモリ上の動作すべきアドレスに展開)し,実行を渡すようにブートローダーを書いてみたのだけど,いまいちうまく動かない.

まあこのへんでいろいろハマってしまいちょっと時間をくってしまった.最初のうちはロードしたプログラムの挙動がなんかおかしくて,スタックポインタの設定とかシリアルの設定とかXMODEMのチェックサム計算とかをいろいろ疑っていたのだけど,じつはそもそもシリアルの受信関数(serial_getc())の内部で改行コードの変換をしているので,バイナリデータが壊れてしまうという問題だった.あー間抜け.(たとえテキストデータだとしても,XMODEMのブロックナンバとかチェックサム部分が壊れてしまい,エラーになってしまう)

ということで実装したのが以下のような感じ.

上のリンクでは,ブートローダーと,ロードして実行させるサンプルプログラム(テキスト入力を受け付けて,それを表示するだけのもの)を配布している.kzload というフォルダがブートローダーで,osというフォルダがサンプルプログラムだ.(将来的にOSをロードするようにするつもりなので,osというフォルダ名にしている)

なので手順としては,以下のようになる.
  1. ブートローダーとサンプルプログラムを別々にビルドする.
  2. ブートローダーを h8write でフラッシュに焼き込む.
  3. ブートローダーを起動する.
  4. loadコマンドでXMODEMでのファイル受信に入り,サンプルプログラムのELFファイルをシリアル転送する.
  5. 転送完了したらrunコマンド(これは今回新設)でサンプルプログラムを実行する,
  6. サンプルプログラムが起動する.
なお上のプログラムでは,実はuuencode形式でのファイル転送もサポートしている.というのは,うまく転送できないのはバイナリファイルで転送しているからでは?と考えて,uuencode形式でファイル転送するようにして試したから,その名残り.まあ実は serial_getc() の改行コード変換が問題だったのでバイナリファイルを転送することに問題は無く,その後 uuencode 形式は使用しないように修正したが,もしも uuencode 形式を利用したいならば以下のようにすればよい.
  • xmodem.c の USE_UUENCODE という定義を有効にしてブートローダーをビルド.
  • サンプルプログラムのビルド時に ./make.sh image を行うことで hello.uu という uuencode 形式のファイルが生成されるので,それを転送する.
デフォルトでは USE_UUENCODE は無効になっているので,ELF形式をそのままシリアル転送すればよいのだけど,なんかうまくシリアル転送できない場合にはuuencode 形式での転送を試してみるといいかもしれない.

ちなみに今回この uuencode 形式にすることを試したときに,モトローラSフォーマットでもいいのでは? と思ってモトローラSフォーマットについて調べてみたのだけど,実は以下のような利点がある.(uuencode形式を選択したのは,単に実装がラクそうだったから)
  1. テキスト形式なので,回線が7bitだったらどうとかフロー制御のコードがあったらどうとか余計なことを考えなくていい.
  2. データを受信しながらそのままメモリ上に展開する,という処理に向いている.
とくに,今回ブートローダーを作っていて,「データをバッファ上に一度読み込んでからロードアドレスに展開するのではなく,データを読み込みながら展開先を調べて直接ロードできないか?」という疑問があった.というのは,いったんどこかのバッファに置いてからロード先にコピーするような動作だと,処理自体は楽なのだけど,まあワーク領域として倍のメモリを使うことになるので,今回のマイコンボードのようにRAMが少ない場合には,問題となるからだ.(結局,今はいったんバッファに置く実装になっているので,これは将来課題ではある)

で,上のような問題意識があったうえでフォーマットを調べていたからこそ今回気がついたことなのだが,モトローラSフォーマットというのは,データを読み込みながらロード先のメモリ上に直接展開するのに非常に向いている.というのは,ロード先のアドレスが先頭にあるので,まずアドレスを読んで,後続のデータをそのアドレスの指す先に配置していけばいいからだ.うーん,きっとこういうことを考えた上で作られたフォーマットなんだろうなあ.

ついでにいうと,これもそーいう問題意識で考えていたからこそ気がついたことなのだけど,ELF形式というのも,まあモトローラSフォーマットほどやりやすくは無いが,プログラムヘッダが先頭付近にあるため,読み込みながら直接展開,ということは原理的にはできる(プログラムヘッダがファイルの終端にあったりすると,最後まで読まないとロード先がわからないので,直接展開は原理的に不可能).

この記事からもわかるのだけど,ELF形式は先頭付近にプログラムヘッダ,末尾にセクションヘッダが配置されており,プログラムのロードはプログラムヘッダを参照して行われるが,リンク作業はセクションヘッダを参照して行われる.なぜこのような配置になっているのか今までとっても疑問だったのだけど,おそらくプログラムヘッダが先頭付近にあるのは上記のようにプログラムを読み込みしながら展開先に直接展開するためだ.あとセクションヘッダが終端にあるのは,これもおそらくだけど,プログラムのロード&実行にはセクションヘッダは必要無いので,サイズ節約のために実行形式から取り除きたい場合がある.この場合,ファイルの先頭付近にあると,そこを削除すると後続のデータのオフセットが変わってしまうため,様々なオフセット計算をやり直さなければならなくなってこれはそうとう面倒臭い.しかし終端にあれば,単にそれを取り除くだけでいいからだ,と思う.

で,難しい話はあとに回して,とりあえず実行してみよう.今までのおさらいも兼ねて,ビルドからひととおり説明する.

まず,ブートローダーとサンプルプログラムをビルドする.ブートローダーは今まで通り,以下でビルドできる.

% ./make.sh clean ; ./make.sh ; ./make.sh image

これで kzload.mot というファイルが生成される.ここまでは(ファイル名が kzload* に変わっているけど)前回通り.

次に,サンプルプログラムをビルドする.

% ./make.sh clean ; ./make.sh

上に書いたように,ファイル転送に uuencode 形式を使うなら,以下も実行しておく.

% ./make.sh image

ここまでで準備は完了.

次に,h8write を使ってブートローダーをフラッシュROMに転送する.実際には Makefile に書いてあるので,make write するだけでいい.

teapot# make write
../../h8write/h8write -3069 -f20 kzload.mot
H8/3069F is ready! 2002/5/20 Yukio Mituiwa.
writing
WARNING:This Line dosen't start with"S".
Address Size seems wrong
WARNING:This Line dosen't start with"S".
Address Size seems wrong
.......................................
EEPROM Writing is successed.
teapot#

これで完了.

cuでシリアル接続し,ディップスイッチを起動用に切替えてリセットボタンを押してブートローダーを起動する.あとでサンプルプログラムを転送するときにファイル指定しやすくするために,cuはサンプルプログラムをビルドしたディレクトリで起動するとよい.

teapot# cu -l /dev/cuad0
Connected
Hello World!
>

ブートローダーが起動して,コマンド入力待ちになる.コマンド待ちするので,ブートローダーっつうよりモニタっつったほうが適切かもしんない.

loadコマンドを実行して,XMODEMによるファイル転送待ちに入る.

teapot# cu -l /dev/cuad0
Connected
Hello World!
> load
(待ち状態)

前回説明したように,「~」「C」でコマンド指定に入り,XMODEM送信アプリとしてlsxを指定する.引数にはサンプルプログラムのELF形式である「hello」ファイルを指定する.(uuencode形式を利用する場合は,ここで「hello.uu」を指定する)

teapot# cu -l /dev/cuad0
Connected
Hello World!
> load
~CLocal command? lsx hello

Enterを押すと転送が始まる.

> load
~CLocal command? lsx hello
Sending hello, 15 blocks: Give your local XMODEM receive command now.
Bytes Sent: 2048 BPS:586

Transfer complete
eceive succeeded.
>



うまく転送できた.実はこの「うまく転送できるまで」でハマッていろいろな試行錯誤があったのだけど,まあうまくいったのでいいや.

今回,ブートローダーにrunコマンドというのを新規追加してある.runコマンドを実行すると,loadによってバッファ上に読み込んだファイルをELF形式とみなして解析し,ロード先のアドレスを調べてメモリ上に展開し,エントリポイントに処理を渡す(要するに,ロードして起動する).

runコマンドで実行してみよう.

> run
starting from entry point.boot succeed!
os>

おー,起動した.かなり感動.ここまで来るのにどれだけ苦労したことか...(いやH8への移植だけに関して言えばここ数日のことなのだけど,OS作りたいなあとむかし漠然と思っていたときから考えると,ホント,しみじみ思ってしまう)

てきとうにコマンドを打ってみる.

> run
starting from entry point.boot succeed!
os> run
run
os> test
test
os> dump
dump
os> load
load
os> aaa
aaa
os>

入力した文字列をそのまま返している.ひとまずちゃんと動いているようだ.runやloadを実行しても,ブートローダーでの動作が行われずにただ文字列をそのまま返しているので,サンプルプログラムに完全に処理が移っていることがわかる.

うーんちょっと感動.結局,ブートローダーが作れてしまった.まあものすごく簡単なものではあるが,きちんと動作して,他のプログラムをブートできている.

OS作りたいなあと漠然と思ってからいろいろ勉強したりしてきたけれど,実際作ろうとなったときにこーいうのがパパッと作れるというのは,やっぱしいままで勉強してきた甲斐があったなあというものだ.しみじみ.やっぱりなんでも基礎力が大事だね!

ちなみに今回のソースコードだけど,ブートローダーには前回に対して以下のファイルが新規追加されている.
  • ELF形式のファイルの解析と展開 (elf.c)
  • uudecode の復号 (uudecode.c)
ここで注目したいのはこれらのファイルサイズなのだけど,elf.cは92行,uudecode.cは48行だ.ちなみにXMODEMの処理である xmodem.c は96行.ELFとかXMODEMとかUUENCODE実装とかってなんか難しそうなイメージあるけど,特定の処理に限定すればこんなもんで済むもんです.これくらいならちょっと読めば誰でも十分に理解できるし,じゃあモトローラSフォーマットに対応してみようとかBinHexにも対応しようとか思ったときに,なんか簡単にできそうな気がするよね.

いま調べたら,ブートローダーぜんぶ含めても634行だった.まあエラー処理とかがかなりザルではあるが,ファイルを読んで展開するだけなら,こんなんで書けるということだ.ブートローダーってなんか難しいイメージあるけど,600行ちょいなら自分でも十分に読めるかな?作れるかな?って感じがするでしょ?OS作るとかブートローダー自作とかってなんか難しいイメージあるけど,あんま難しいこと考えずに作れば,これくらいでできちゃうもんなのよ.

あと今回思ったことだけど,ELF形式についてなのだけど,実際に自分で作ってみないと気が付かない,逆に言えば自分で作ってみれば気がつくこと,というのはあるもんだなあ,と.ELF形式のプログラムヘッダがなぜ先頭付近にあるのか?セクションヘッダがなぜ終端にあるのか?は,今回実際にいろいろ作ってみて(試行錯誤してみて)はじめて気がついたことだ.

よく,通り一辺倒の知識を得ただけで「この分野に関しては制覇したぜ!」みたいなこという人がいるよね.「C言語は完璧だぜ!」とか言っちゃう感じ.でもそーいうのって全然不十分で,そもそも本に載っている知識を得ただけで満足するだけじゃだめで,なぜそのような仕様になっているのか?なぜそーいう実装になっているのか?という製作者の考えとか気配りとか苦肉の策とかに気がつくようにならないと,その分野をマスターしたとはいえないなあ,と思う.こうこういう経緯で,歴史の流れでそうなってしまった,とかね.(まあ別に自分がELF形式をマスターしたぜ!という意味ではなく,自分に対する戒めです).

C言語なら,文法事項をマスターしただけでなく,なぜそのような言語仕様になっているのか?とかね.こーいうのは習って知るだけではなく,作られた経緯とか歴史とか実際の使われかたとかを勉強していく過程で,自分で自然と気がつけるようになりたいものだ.1を知ることで10を知る,って感じでね.

今日はとりあえず動いたので満足.サンプルプログラムの構成とか詳しい解説は次回にしよう.
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

前回でシリアル送受信ができるようになったのだけど,次はシリアル経由でのファイル転送ができるようにしたい.で,ファイル転送用の独自プロトコルを作るか適当な既存のプロトコルを実装するかだが,まあ独自プロトコルにしてしまうと送信アプリを書く必要が出てきてしまい,さらにその送信用アプリをLinuxでコンパイルして使うにはとかWindowsから使うにはどうすればいいかとかいった話になってしまって,それはそれでとっつきづらいと思うので,適当な既存のプロトコルを実装することにする.

で,プロトコルなのだけど,XMODEMというのが簡単そうなのでそれにしよう.まあXMODEMならLinuxとかWindowsとかでもいっぱい送信用アプリがあるだろうし.(WindowsならたぶんTeraTermでファイル転送できるだろうと思う)

ちなみにXMODEMにはファイルの終端が Ctrl-Z で終ってしまっている場合にそれを検出できないという問題があるようだが,実行形式ファイルのお尻がわからないだけで,実行形式ファイルの展開という意味ではとくに問題は無い.(実行形式は内部に独自にヘッダ情報を持っていて,それを見てメモリ上に別途展開するので,終端に余計なものが付いていても別に問題ない)

XMODEMについては,以下が参考になる.他にも改良版のYMODEMとかZMODEMとかあるらしいが,まあ転送するファイルのサイズもそれほど大きくはならないだろうから転送効率とかあんまし考えなくていいし,エラー時再送とかきちんとしなくても(信頼性が低くても)もう一度やりなおすだけだし,とりあえず実装が一番楽そうなXMODEMを実装する.

で,上のホームページを見て実装したのがこんなかんじ.

xmodem.c というファイルを追加してあってその中でファイル受信しているのだけど,xmodem.c はなんとたったの89行.うーん,簡単なプロトコルなので楽でいいのう.ちなみにファイルの転送先なのだけど,とりあえず loadbuf[] というバッファを適当なサイズで定義して,そこにそのままコピーするようにしてある.まあ対して難しいプログラムではないので,詳しくはソースを見てちょうだい.(現状,Cファイルとヘッダファイルだけで400行程度なので,十分に読み切れると思う)

あと細かいところをいろいろ修正してある.
  • H8の書籍とかちゃんと読んで,シリアルの送受信処理を見直し.とくに受信時にSSRのRDRFを落とす処理を追加.(これをやっていなかったので,連続して受信したときに読み出しデータがおかしなことになっていた)
  • リンカスクリプトを,もうちょっと修正.(MEMORYコマンドにより,ROM領域とRAM領域を明示的に定義するようにした.これにより,ROMやRAMのサイズ不足で溢れたときにはリンカがエラーにしてくれるようになる)
  • XMODEMでファイル受信するコマンドとして,loadコマンドを追加.あと受信したファイルの内容チェックのために dump コマンドを追加.
  • ライブラリ関数とかをこまごまと修正.
で,ビルドしてフラッシュROMに焼いて実行してみる.フラッシュROMへの転送のしかたは第1回を参照.

ちなみにフラッシュROMへの転送なのだけど,以下の手順で電源のOFF/ON無しでできるようだ.
  • フラッシュROMへの書き込み
    1. ディップスイッチを左からON,ON,OFF,ONにする.
    2. cuで接続している場合は,抜ける.(これをやらないとh8writeが動作できない)
    3. リセットボタンを押す.(電源OFF/ONは不要)
    4. h8writeで書き込む.
  • 書き込んだプログラムの起動
    1. ディップスイッチを左からON,OFF,ON,OFFにする.
    2. cuで接続する.
    3. リセットボタンを押す.(電源OFF/ONは不要)
    4. 実行が開始される.
上のような感じで,ディップスイッチを切替えてリセットボタンを押すだけで,電源のOFF/ON無しでフラッシュへの書き込みとプログラムの実行を繰り返すことができる.

で,以下,実行結果.

teapot# cu -l /dev/cuad0
Connected
Hello World!
>

cuでつないでからリセットボタンを押して起動すると,まず Hello World が表示される.

ここでXMODEMでのファイル受信用の「load」コマンドを実行する.

> load

コマンド実行すると,XMODEMでの受信待ちに入る.

で,FreeBSD側からのXMODEMでのファイル転送なのだけど,PowerPCのときも使った「lrzsz」を使う.インストールされていない場合はまずインストールする.(FreeBSDでは packages になっているので,パッケージインストールできます)

で,ファイル転送なのだけど,cuは「~C」を入力することで別コマンドを fork してその入出力を繋げることができるので,その機能を利用して lrzsz にXMODEMプロトコルをしゃべってもらってファイル転送する.まあ手っ取り早く言うと,loadコマンド実行後に,まず「~」「C」のようにキーボードから入力する.

> load
~CLocal command?

「Local command?」のように実行するコマンドを聞いてくる.lrzszでは,「lsx」というコマンドがXMODEMのためのコマンドなので,lsxを指定する.さらに,送信するファイルを引数として指定する.ここでは a.txt という適当なテキストファイルを作成して指定してみる.

> load
~CLocal command? lsx a.txt

ちなみにa.txtの内容は以下の通り.ほんとてきとう.

abcdefg
hijklmn

これで Enter を押すと,ファイル転送が開始される.

> load
~CLocal command? lsx a.txt
Sending a, 0 blocks: Give your local XMODEM receive command now.
Bytes Sent: 128 BPS:142

Transfer complete
eceive succeeded.

ちなみにWindowsとかだとXMODEMでファイル転送するためのアプリがきっといっぱいあるだろうから,それを使えばいいだろう.その場合,転送プロトコルは「XMODEM」で,転送ブロックサイズは128バイト,チェックサムは1バイト(8ビット)の単なる加算のもの(CRCチェックサムではない)を指定する.正式には「XMODEM/SUM」というらしい.(「XMODEM/CRC」とか「XMODEM/1k」だとダメなので注意)

ファイルが転送できたら,dumpコマンドでファイルの内容を確認してみる.

> dump
size: 128
61 62 63 64 65 66 67 0a 68 69 6a 6b 6c 6d 6e 0a
1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a
1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a
1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a
1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a
1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a
1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a
1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a

>

a.txtの内容がばっちり書き込まれている.(128バイト単位で,お尻はCtrl-Zで埋められる)

今回は実は最初はぜんぜんうまく動作しなかったのだけど,シリアルの受信処理に問題があっておかしなデータが受信されていたのが原因で,そこを直したらXMODEMの処理自体はあっさりと動いた.うーん楽でいいねえ.

これでシリアルからのコマンドを受け付けて,XMODEMでファイル転送できるようになった.なんかモニタっぽくなってきたね.次は実際にプログラムを転送して,動作できるようにしたい.オブジェクトファイルフォーマットを何にしようかなあ...やっぱしELFかなあ...
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

前回はシリアル送信で hello world が出せたので,今回はブートローダーを自作する準備として,シリアルの送受信をやってみる.というのは,今回自作するブートローダーは,シリアル経由でOSの実行形式ファイルをダウンロードしてRAM上に展開して実行,という動作にするつもりなので,シリアル経由での操作が必須になるからだ.

まあ前回作成したソースコードで,シリアル受信の関数 serial_getc() がすでに作ってあるので,main.c をちょいちょいと適当に修正して,起動したらプロンプトを出して文字列が入力されたらそれをそのまま返すようなプログラムにしてみた.以下のような感じ.

で,ビルドしてフラッシュROMに焼いて実行してみる.フラッシュROMへの転送のしかたは前回を参照.

teapot# cu -l /dev/cuad0
Connected
Hello World!
> test
test
> sample
sample
> test
test
>

うーん,あっさり動いた.

とりあえずこれでシリアルの送受信はできるようになったので,いよいよブートローダー作成なのだけど,何点か決めなきゃならんことがある.

まず,ブートローダーは起動したらプロンプトを出して,入力されたコマンドに応じて動作するというような動作にするつもり.で,コマンドには「シリアル転送でプログラムの実行形式をダウンロード」とか,「ほげほげ番地にダウンロードしたプログラムを実行」みたいなのを作る.つまりコマンドベースで動作して,プログラムのダウンロードや実行を行えるようにする.ちなみにこーいうのを一般的に「モニタ」とか言ったりする.

で,決めなきゃならんのは,シリアル経由でのプログラムのダウンロード方法だ.世の中にはシリアル経由でファイル転送するためのXMODEMとかYMODEMとかいう方式がいくつかあるのだけど,
  • (案1)XMODEMあたりをてきとうに実装する.
    • 長所...世の中になるXMODEMに対応したファイル転送アプリを使って転送できる.
    • 短所...とくになし.
  • (案2)てきとうな独自プロトコルを作って実装する.
    • 長所...好きにプロトコルを作れるので,どうにでもなる.
    • 短所...ファイル転送アプリを自作する必要あり.
うーんどうしようかなあ.

あと,転送するプログラムはブートローダーがRAM上に展開するのだけど,これも実行形式ファイルのフォーマットを何にするか決めておかないといけない.実行形式ファイルはELF形式とかモトローラのSレコード形式とかCOFF形式とかあるけど,そーいう一般的な形式にするか,それともオリジナル形式にしてしまうか.

うーん悩みどころだ.
(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

ここのところOSC関西OSC名古屋に出展したが,やっぱし実機で動いているとインパクトあるので,ボードコンピュータへの移植を進めたいなあと思っていたのだがちょっと時間がとれたので進めてみた.ようやく第一段階にたどりついた感じ.

まずはターゲットボードの選定なのだけど,いろいろ探したのだけど,秋月電子H8/3069Fネット対応マイコンLANボード(完成品)というのに決めた.理由は以下.
  • なんといっても安い.(完成品だと3750円,キットだと3400円.これなら中学生や高校生でも買える)
  • 完成品の形で売っていて,シリアルコネクタが実装済みなので,そのまま使える(半田づけが完全に不要).(もちろん,キットで買って自分で組み立てれば安く済む)
  • フラッシュ書き換え回路が実装されているので,ROMライタなどを使わずにシリアル経由でフラッシュROMを書き換えられる.(フラッシュをとばしても安心)
  • H8は巷に書籍や資料がたくさんある.日本語資料も豊富.(これは,中学生にはとても嬉しいはずだ.英語資料だと中学英語の知識で読めるかどうか微妙なので)
  • 内蔵ROM,内蔵RAMの容量がそれなりにある(ROM512KB,RAM16KB).H8/3048だとRAMが4KBでいろいろやるにはちょっと不安.
ちなみに電源アダプタは別売りなので,5Vのやつを一緒に買う必要があります.これは秋葉原の秋月の店舗で買うなら,適当なのを一緒に買いましょう.

買ってから気がついたのだけど,付属のCD-ROMにフラッシュROM書き換えソフトがついているのだけど,これがCのソースで配布されていて,FreeBSDでもあっさりコンパイルできて動作した.なのでWindowsに頼らずに,完全にFreeBSD上で開発ができます(これはぼくにはとても嬉しい).

ちなみに秋月でもうひとつ,SH2の似たようなボードがあってそっちも候補だったのだけど,フラッシュROM書き換えソフトがWindows用なのでそれを使わないとならない.まあブートローダーだけ焼いてしまえばあとはOS開発では使うことはないのだけど,ちょっとイマイチではある.(実はそのSH2のボードも一緒に買ったのだけど,最終的に上記の理由が決定打になってH8ボードをターゲットに決めた)

で,まずはクロスビルド環境の作成なのだけど,まあこれは付属のCD-ROMにtoolchainが一式が入っているのだけど,当然ながらWindows用かLinux用になってしまう.やっぱしFreeBSD上で開発したいし,まあクロスコンパイラくらい自分で用意したいというのもあるので,いつもどおりbinutils+gccの組合せで,クロスビルド環境を構築する.

OSの起動には,ブートローダーをどこかから持ってきて動かして,OSはシリアル経由でダウンロードして動かす,という構成が開発には都合がいい.ブートローダーなのだけど,eCos付属のRedBootというのがH8に対応しているらしい.

ということで当初はRedBootを移植する予定だったのだけど,まあ結論から言っちゃうとRedBootは使わずに自前でブートローダーを作成することにしました.

なぜかというと,RedBootってC++で書いてある部分があるのでC++のクロスコンパイラが必要になる.しかしgccのクロスコンパイラ作成時にc++有効にすると,ライブラリがなんか必要になってしまうようで,newlib入れたりとかいろいろ試したのだけどうまくクロスコンパイラが作れなかったのよ.c++を外すと簡単にコンパイラが作れるのだが.

で,たとえこれでなんとかc++のクロスコンパイラを用意したところで,この文書を読んで試すひとが同じようにはまったりしてもつまらないし,なんかクロスコンパイラ作成にひと苦労ってものなんだかな~って気がするし,組み込みOS作るならブートローダーくらい自作しても面白いんじゃないかと思う(OSの動作だけでなく,ブートストラップの勉強にもなる)ので,自作してみることにしました.ちょうどCQ出版からブートローダーの本が出たことだし.(CQは,相変わらず良い本を出すね!みんな買いましょう!)

で,話がそれたけど,まずはクロスビルド環境の構築なのだけど,H8用のクロスのbinutilsとgccは以下で構築できた.ちなみに環境はFreeBSD-6.2ね.

■ binutils-2.19 をインストール

% ./configure --target=h8300-elf --prefix=/usr/local --disable-nls
% gmake

# gmake install



■ gcc-3.4.6 をインストール

% cd gcc-3.4.6
% setenv SHELL /usr/local/bin/bash (シェルがbash以外の場合)
% ./configure --target=h8300-elf --prefix=/usr/local --disable-nls --disable-threads --disable-shared --enable-languages=c
% gmake

# setenv SHELL /usr/local/bin/bash
# gmake install

あとフラッシュROM書き換え用のアプリなのだけど,h8write というのがCD-ROMに付属しているのだけど,これはネット上のOpen SH/H8 writerで配布されていて,しかもFreeBSDでも動くらしい.なのでいちおうネットのほうから最新版を持ってきて,以下の修正をしてビルドした.

% gcc h8write.c -o h8write

これであっさりビルド完了.

--- h8write.c~ Mon Aug 24 13:45:33 2009
+++ h8write.c Mon Aug 24 13:45:52 2009
@@ -1,6 +1,6 @@
-#define LINUX
+#undef LINUX
#undef Solaris
-#undef FreeBSD
+#define FreeBSD
#undef Win32
/*******************************************/
/* EEPROM write program to H8/300H */
@@ -42,7 +42,7 @@
#define RSLINE "/dev/ttyS0"
#endif
#ifdef FreeBSD
- #define RSLINE "/dev/cuaa0"
+ #define RSLINE "/dev/cuad0"
#endif
#ifdef Solaris
#define RSLINE "/dev/cua/b"

#define でプラットホームを指定しているけど,結局はシリアルデバイスをOSごとに指定しているだけだったりする.ちなみにFreeBSDではFreeBSD-4.x系は /dev/cuaaXだけど,FreeBSD-6.x 系は /dev/cuadX になるので注意.(でも実行時にデバイス指定できるみたいなので,そうして使うならこの修正もいらないかも)

これで,ビルド環境はそろった.あとは以下あたりを参考にして,てきとうにサンプルプログラムを書いてフラッシュに焼いて動作させてみる.上の「H8マイコンLANボードではじめる...」のほうに hello world のサンプル(h8_sci_rom.lzh)があるので, make.sh とか作ってとりあえずそれを動かしてみたらあっさり動いた.ただし上記サンプルでは,スタートアップでスタックポインタの設定がされていないので,ちょっと不気味ではある.(設定忘れだと思う)

ちなみにフラッシュへの焼き込みだけど,以下のようにする.
  1. プログラムのビルドに成功するとELF形式の実行形式が作成される.これをobjcopy を使ってモトローラSフォーマットに変換する.以下のような感じ.

    objcopy -O srec sample.elf sample.mot

  2. シリアルのストレートケーブル(シリアル延長ケーブル)でPCとボードを接続する.(クロスケーブルは不要)
  3. ディップスイッチを左からON,ON,OFF,ONにして起動.
  4. 作成した redboot.mot を h8write で焼く.

    h8write.exe -3069 -f20 redboot.mot

  5. 書き込み時に

    WARNING:This Line dosen't start with"S".
    Address Size seems wrong

    とかいうワーニングが出てたけど successed と言われたので,とりあえず気にしない.
  6. 以下のようにしてcuとかで38400bpsで接続する.

    # cu -s 38400 -l /dev/cuad0

  7. ディップスイッチを左からON,OFF,ON,OFFにしてリセットすることで,プログラムが起動する.リセットボタンを押すたびに hello world が表示される.
注意として,cuが起動しっぱなしで繋がったままだと,h8write で新たに書き込もうとしても書き込みできないので注意.h8write で書き込む際にはcuを切ること(シリアルがcuに占有されるので当然なのだが,ぼくはこれでけっこうハマった...).ちなみにcuは「~」「.」を押すことで切れます.

ちなみにCD-ROMにH8/OSというOSが付属していて,プログラムをメモリ上にロードして実行する機能がある.さらにputというコマンドがCD-ROMに付属していて,FreeBSDにも対応しているみたい.(h8writeと同様に,先頭にFreeBSD用の#defineがある)

ということでH8/OSをフラッシュに焼けばブートローダーとして使えて,putでKOZOSを送って実行する,ということができそう.まあでもネタとして面白いので,ブートローダー自作しようかとは思ってはいる.

で,まあとりあえず hello world くらいは全部自分で書いてみたいのと,上記サンプルプログラムはグローバル変数の書き込みができないはずなので,hello world を自分でスクラッチで書いてみた.以下のような感じ.

以下の考慮をしてある.
  • リンカスクリプトを自前で用意.
    • リンカスクリプトに関しては,付属のCD-ROMにあるH8/OSで使われているものを 流用できて,上記サンプルもそれを利用しているみたいなのだが,以下で説明する ように VADDR と LADDR を別にする必要があるのでそのままでは使えない.なので せっかくなので自分で書いてみた.
  • グローバル変数の書き込みに対応.
    • 上記サンプルはROM上で動作する前提なので,グローバル変数もROMに配置されて しまっていて書き込みできない(はずだ).なのでグローバル変数の初期値はROM 上に持って,起動時にRAM上にコピーしてそっちを使うように修正. (このためグローバル変数に関しては VADDR ≠ LADDR になるので, リンカスクリプトで工夫している) (グローバル変数のVADDRやLADDRが期待通りに配置されているかどうかは, readelf や逆アセンブル結果を参照して確認した)
  • スタートアップでスタックポインタを設定.
  • 割り込みベクタテーブルをリンカスクリプト側で作成せず,配列として定義. (このほうが一般の人は修正しやすいんではないかな,と)
  • ビルド用に make.sh を用意.(これは PowerPC で使ったやつを適当に修正して 利用)
  • シリアル関連とかをライブラリ化してソースコードを整理.
まあこんなところかな.

ビルドの方法は簡単で,make.sh というスクリプトを用意してあるので,以下のようにするだけです.PowerPCのと同じ感じ.

% ./make.sh clean
% ./make.sh
% ./make.sh image

./make.sh image することで,sample.mot が作成されます.で,スーパーユーザになって

# ./make.sh write

で,h8write を起動してフラッシュに書き込みます.シリアルは9600bpsで接続.

ちなみに以下が実行結果.グローバル変数の書き換えもばっちしうまくできてるみたい.

teapot# cu -l /dev/cuad0
Connected
Hello World! 10 1234 0 1 1 10
Hello World! 10 1234 0 1 1 10
Hello World! 10 1234 0 1 1 10

夏だけど、鍋だ。


鍋をするときにぼくがまず考えるのは、買出しとか準備とかが楽で簡単で、そしておいしいことだ。そういった点で、キムチ鍋を越える鍋はそうそう無いと思う。


まず、つけダレがいらない。何を入れてもたいていおいしい。肉少なめでも野菜だけでもけっこう満足できるのでヘルシーで安上がり(実際、安く済む)、作り方が簡単でだれでも作れる、どう作ってもまず失敗しない、うーんすばらしい鍋だ。(つけダレがいらないし簡単なので、花見とかキャンプとかでもおすすめです)


で、最近好きなのがキムチのしゃぶしゃぶだ。とは言ってもぼくが勝手にやっているオリジナル鍋なのだけど、以下のような感じだ。


・まず、鍋にお湯を張ってわかす。
・キムチを軽くゆがいて、しゃぶしゃぶ状態で食する。
・キムチをゆがいているうちに、キムチエキスが鍋に溶け出してキムチ鍋になるので、あとはてきとうに具財を入れてキムチ鍋にして食する。


キムチってそのまま食べるとちょっとしょっぱくて塩分取りすぎになっちゃうかなーと思うので、ゆがくことでまわりのキムチ成分を落として、うまみはそのままにシャキシャキした白菜の歯ごたえを楽しむことができます。白菜の芯の部分がシャキシャキしてておすすめ。シャキシャキ感を楽しむために、あまりゆでないこと。あくまでしゃぶしゃぶで。


「キムチ鍋のもと」は、はっきり言って要りません。不要です。ていうかキムチ鍋にキムチ鍋のもとを入れると、しょっぱすぎると思う。毎回同じ味になってつまらないし、キムチだけでいきましょう。ぜったいにキムチだけのほうがうまいです(たとえそれで味が薄いとしても)。むしろキムチのもとに頼らずに、スーパーで売ってるいろんなキムチを試すほうが、やってて楽しいです(安くてうまいのがないか、探す楽しみがあります)。キムチ鍋のもとを買うお金は、そのぶんキムチを買うほうに使いましょう。


ただキムチはそれなりに高いものにしたほうが無難かも。甘みのあるようなやつだとちょっと微妙。おすすめは以前ブログに書いたように、しばらくほっといて酸っぱくなったキムチを使うことで、これがいいかんじで酸っぱくなってれば、もとがどんなキムチでもOKです。もとが甘みのあるキムチでも、これならぜんぜんOK.方法は簡単、冷蔵庫で1週間~1ヶ月置いておくだけです。(以前は室内放置とも書いたけど、常温だとちょっとアンモニア臭くなって、これって食っても大丈夫なのかと不安になるので、初心者は止めたほうがいいでしょう。私はそれでもよろこんで食ってますが)


肉は、あまり入れなくても大丈夫です。せっかくなのでヘルシーにいきましょう。肉を買うお金は、まずはキムチにまわしましょう。ものたりないというひとは、まずは豆腐を入れて、肉は少なめにするとヘルシーでしょう(豆腐でもけっこう満足できます)。それでもものたりなければ、厚揚げを入れてみましょう。肉を入れるなら、豚肉か鶏肉かな。牛は合いません。鴨肉もおすすめ。うまいよ。


基本的には野菜を楽しむ鍋です。で、野菜は何をいれてもおいしいのだけど、まずはホウレンソウがおすすめ。豚肉にあうし、ゆでたての緑色のホウレンソウってとにかくうまいのよ。あと春菊とか、小松菜とか、緑色で味の濃い野菜がおすすめ。チンゲンサイとかもいいよ。基本は茹でたてて、色が青いうちに食べること。シャキシャキの状態で食べること。くたくたになるまで煮るとふつうの鍋になっちゃうし、味の濃い野菜だと、その味が鍋に出てしまうので。


ゆでたての野菜って、ホント、うまいよ。そして目の前で自分の好みの状態にゆでた、ゆでたての野菜をそのまま食べられる料理は、鍋だけです(だから鍋はすばらしい)。お店の料理はお客の好みの最大公約数の状態にゆでられるわけだし(まあホントの高級店は、お客の個々に合わせて料理するっていうけどね)、厳密にはゆでたてのそのままの状態では食べられないし、これはどんな高級料理もかないません(そりゃそうだ、目の前で自分で作ってるんだから)。ゆでたてのきれいな緑色の葉っぱとか、しゃきしゃきの茎とか、ホント、うまい。


野菜はスーパーに行って、今日はこれを入れてみようみたいな感じで目に付いたものをてきとうに入れてみると、あーこれはこんな味なんだなーといろいろ楽しめます。モロヘイヤとかあしたばとかもやってみると、けっこうよかったりします。また野菜も部位によってぜんぜん味が違ったりして、これも発見があって楽しめます(私はこれで、ホウレンソウの茎の部分と根元の部分のうまさに目覚めました)。この鍋をやる前提でスーパーに買出しに行くと、普段目がいっていない野菜の種類の多さに驚くことができます。(野菜はほんとうに何を入れてもたいてい大丈夫なので、選ぶ楽しみと工夫する楽しみとチャレンジする楽しみと、失敗する楽しみがあります。失敗するのも楽しいよ!)


こないだチャレンジしてけっこうよかったのが、みょうが。しゃきしゃきしてて味も濃くてとってもうまい。鍋にみょうがって普通はありえないけど、こーいうのを試せるのは、大きな楽しみだ。いろいろ試すのはホントおすすめ。

豆も入れるととってもうまい。豆は、ほんとにおすすめ。枝豆とかの剥き身のやつとかだと楽です。あといろんな豆のゆでたのの詰め合わせみたいのをスーパーで売ってたりするけど、そーいうのもおすすめ。ポリポリしてうまいよ。さやつきインゲンをサヤごととかね。(もちろん失敗もあるが、工夫すればうまくなったりやっぱりダメだったりで、それはそれで楽しめる)


キノコ類はなんでもいいけど、けっこうダークホースでうまいのが、なめこ。鍋にはあんまりいれないキノコだけど、これがいれるとひじょーにうまい。切らなくていいので準備も楽。なべになめこって聞いただけでえーそれって大丈夫なの?と思いそうだけど、この鍋はどんなものでもたいてい、大丈夫です。恐れずにまずは入れてみましょう。(ダメなら入れるのをやめるだけ)


魚介は。。。あまり入れたことは無いです。うーん入れても鍋の味に負けてしまうような気がするなあ。。。入れるなら、イカとかエビとかかしら。ゲソとかならうまいかも。マグロとかでもいけるかも。


あーあとアスパラはうまいのだけど、ブロッコリーはちょっと微妙だったなあ。。。ぼくはどっちも好きなので、絶対いけると思ったのだけど、ブロッコリーはなんつーか、飽きます。まあでもアスパラもブロッコリーも、うまいです(ブロッコリーは飽きるけど)。ゆでたてできれいな緑色のときにそのまま上げて食べるのがたまらないです。ホント、ゆでたてってうまいよ。(今思ったけど、とうもろこしとか案外いいかもね。ゆでたてで)


セロリは期待してたのだけど、微妙だったかな。セロリはむしろ前に書いた「すっぱ辛鍋」に合うかなあ。エスニックな感じがして。


たとえて言うなら、あれだ。バーベキューで焼きたいものを思いついたまま焼いてみる感じ(チーズとか、クリームパンとか、マシュマロとか)。失敗だったり精巧だったり、失敗でも焼き加減によって別の味になったり。あれが、部屋で楽しめます。


思いつくままてきとーに書いちゃったけど、ぼくは鍋ってけっこう好きなので、夏でもやります。食材を選ぶ楽しみがあっていいよ。冬になると鍋セットとか鍋用カット野菜とかスーパーで売ってるけど、やっぱし自分で食材選んで、失敗したり成功したりするのが楽しいと思います。自分で選ぶと失敗も多いけど、新たな発見がすごくたくさんあって、ホント、楽しめます。おすすめ。


鍋というと失敗しないように保守的にいく場合も多いけど、楽しむならばチャレンジングにいきましょう!そのほうが楽しいよ!失敗も楽しいよ!失敗大好き!(マゾか!?)