前回、構造化プログラムでポート2に接続したLEDを点灯 ステートマシンで簡単にプログラム
するプログラムを作成した哲さん。今日は、Appliletを使い
アセンブル、リンクまでするのかと思い巡らしながら、歩き
啓二さんの部屋の前までやってきました。ロボは、おすわり
で、部屋のドアが開くのを待っています。ドアをノックして
ノブを回し、部屋へと入ります。
哲 :押忍。来たぞ。今日は、Appliletを使うのか。 啓二:いや、まだだ。 哲 :先週作ったプログラムを、動かしてみたいんだがなあ。 啓二:慌てるなって。前回のプログラムを、別の視点で眺めてみるのさ。 あのプログラムは、ステートマシンを使っていることになる。 哲 :ステートマシンって何だよ。 啓二:ステートの意味は、状態。その状態を、遷移させて、プログラムに なるからステートマシンさ。ある状態になったら、状態に与えられた 動作をする。ステートマシンは、シーケンサと呼んでもいいが。 哲 :シーケンサねえ。そう言えば、マイクロプログラムのときにも、そんな 言葉が出てきたぞ。 啓二:そうだなあ。ステートマシンは、どちらかというと、ハードウエアよりの 用語かも知れない。シーケンサならば、意味はわかるだろう。 哲 :わかる、わかる。ラダーシーケンサを少し勉強したからな。 啓二:よし、よし。じゃあ、前回のプログラムを見てみよう。 MOVW HL,#LEDTAB ; LOOP_START: MOV A,L ; HL <- HL + offset ADD A,B MOV L,A MOV A,[HL] ; get bit pattern XOR A,#0fh ; invert MOV P2,A ; INC B ; MOV A,B ; A <- B CMP A,#6 BNZ $LOOP_END MOV B,#0 ; B <- 0 LOOP_END: BR $LOOP_START 哲 :あれれれ、1週間たったら、何をしているのか、わからなくなったぞ。 啓二:だろう。そういうときのために、ステートマシンを図で描いておく。 特にアセンブリ言語で書くとプログラムは、わからなくなる。 哲 :何をやるのかは、仕様でわかっているのに、コードが理解できない。 啓二:じゃあ、ステートマシンをかくか。 これで、わかるようになっただろう。 哲 :うん、うん、良くわかるぞ。ビットパターンを用意して、それを ポート2に順次送っていくんだ。そして、全ビットパターンを送 り終えたら、また最初から同じ動作を続けるんだ。 啓二:ステートマシンを使うときには、各ステートに数値を与えて、その 数値を順番にたどっていく。そして、ある数値から最初に戻る。 これならば、動作を追加、削除も簡単だ。 哲 :装置のボタンを押して、指定の動作を間違いなく実行させたい とき、ステートマシンが便利だな。 啓二:マイコン応用装置に、大抵、ステートマシンが入っている。 マイコンを使わないで、CPLDやFPGAを利用した装置でも、 ハードで、ステートマシンが入れていることが多い。 哲 :じゃあ、もう一度ステートマシンの観点で、LED点灯テスト プログラムを見てみるか。 啓二:よし。まず、ステートマシンを構成するときには、ステートを 表現する変数が必要なんだ。上のアセンブリ言語プログラムなら コメントに入れてあるoffsetが、ステート変数だ。 哲 :offsetで、状態を記述してあるから、対応動作を決められる。 コンピュータは、数値しか扱えないから、この方法が無駄がないね。 啓二:で、ステート変数に対応した動作を記述する。詳細は、後で ゆっくり考えればいい。動きを分解して、その動作を記述する。 機械的な変換作業にブレークダウンできるから、バグも出にくい。 哲 :ステートマシンは、システムに唯一なのか。 啓二:いや、そんなこともないよ。自分の経験では、3つ程度の階層 構造にして、層によっていくつかステートマシンを用意してもよい。 層構造を取れば、そのステートマシンが実現する範囲が明確になる。 通信のプロトコルスタックを実現するときには、OSIは7層用意 しているよな。あれを思い浮かべると、わかりやすいぞ。 哲 :ステートマシンと言っても、実際に利用されているんだなあ。 コンピュータの扱う対象が違うだけで、必須となる概念は、驚く くらい少ないようだし。 啓二:そうかも知れない。ステートマシンは、マイコンの中で、たくさん 利用されているし、ハードウエア動作に向いた概念かな。 哲 :ところで、LED点灯テストプログラムを、C言語で記述すると こんなのかな。 state = 0 ; while (1) { val = get_pattern( state ); send_pattern( val ); state++ ; state %= 6 ; } 啓二:そんなところでいいんじゃないか。関数で、ハードウエアを 見えなくしているのが、お前らしい。 哲 :いやあ、そうでもない。ただ、単純にMS-DOSやLinuxの環境で 動作論理を調べられるようにするには、どうしたらいいかと 考えたら、自然にこうなったんだ。 啓二:じゃあ、LSICで動作するような、プログラムを作成してみるか。 哲 :そんなことできるのか。 啓二:簡単さ。ハードウエア部分を画面でエミュレートすればいい。 ちょっと待っていろよ。 (10分ほどで完成) #include <stdio.h> unsigned char get_pattern(int x) { unsigned char pat[6] = {1,2,4,8,4,2}; return *(pat+x) ; } void send_pattern(int x) { int i ; for ( i = 7 ; i > -1 ; i-- ) { putchar('0'+((x >> i) & 1)); } putchar('\n'); } int main(void) { int state,val; state = 0 ; while (1) { val = get_pattern( state ); send_pattern( val ); state++ ; state %= 6 ; } return 0 ; } ほいよ。 哲 :できたか。あれま、えらく単純。 啓二:そうさ。単純な方が理解しやすいだろう。 哲 :ビットパターンは、配列から添字を使って引き出すのか。 で、2進表現にするため、シフトと論理演算を利用。 いや、本当に簡単だ。 啓二:これを、コンパイルして、コンパイラにアセンブリ言語の コードを作成させてもいい。 哲 :何と、そういう手があったか。確かに、コンパイルのオプ ションスイッチに、そんなのがあったなあ。 啓二:今でも、ソースコードが、思った通りの動作に変換されて いるかどうかを、アセンブリ言語で見ているプロも多い。 哲 :それで、何かメリットがあるのか。 啓二:あるよ。ソースコード記述によっては、冗長なことをして いることもあり得る。それを見つけて、単純に、高速にと 記述の仕方を変える。気にいらなければ、そっくり、アセ ンブリ言語で記述してしまう。 哲 :そんな細かいことまで、担当するのか。 啓二:チューニングにより、目的の性能が出るなら、何でもするさ。 C言語にしてわかったんだが、あのプログラムじゃ、テスト できないなあ。 哲 :何故だ。 啓二:速すぎて、人間の目では、認識できない。 ウエイトを入れないと、動作がわからない。 哲 :ああ、そうだなあ。どうしようか。 啓二:サブルーチンで、空ループを回してディレイにしよう。 来週、それをやろうぜ。じゃあ、ここまでにしよう。