サブルーチン内で変数を使用する場合、「byval」と「byref」という2通りの設定があります。まだちゃんと理解できていませんが、これらの違いについてわかったことを書きます。
サブルーチンのテストプログラムではN1とN2という2つの変数を用いています。ひとつにするとうまくいきません。今回のプログラムの主要部分をサブルーチンなしで書くと下記のようになります。
このプログラムで、Forループ1回目から8回目までのN1, N2, Portaの値はそれぞれ下の表のようになります。
回数 | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 6回目 | 7回目 | 8回目 |
N1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
N2 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 |
Porta | 1 | 3 | 7 | 15 | 31 | 63 | 127 | 255 |
これを変数ひとつで書くとすると、下記のようなプログラムが考えられます。
「2のN1乗」を新たな「N1」とし、ポートAに「N1-1」を出力します。プログラムの文法的には間違っていないのですが、これでは意図した動きになりません。Forの行の「N1」とNextの行の「N1」が異なった値になってしまうからです。
Forループ1回目は「N1=1」です。次の行で「2^N1」つまり「2」が新たな「N1」になります。ポートA出力は「N1-1」なので「1」になり、LED0が点灯します。ここまではうまくいきます。しかしこの時点で「N1」はすでに「1」ではなく「2」になってしまっているので、次行「Next N1 (次のN1)」は「3」ということになります。Forループ2回目は、「N1=2」ではなく「N1=3」としてプログラムが実行されます。「N1=3」だとポートAは「7」になり、LED0〜2の3個が点灯してしまいます。
変数に関する計算をサブルーチンの中へ移しても結果は同じです。メインプログラムから受け取った「N1」の値を「2^N1」に変えてForループの中へ戻すので、やはりうまくいきません。こういうわけで、どうしても変数を2つに分ける必要がありました。
BASCOM-AVRのマニュアルを見て、サブルーチン内で変数を用いるとき、「byval」と「byref」という2通りの設定方法があることを知りました。で、今回のプログラムの場合、「byval」を指定すると変数がひとつでもうまくいくことがわかりました。マニュアルによると、「byval」は「変数を値によって渡す」、「byref」は「変数を参照によって渡す」なんだそうです。何のことやらよくわかりませんが、ともかく、変数を「byval」にするとうまくいきます。なお、「byval」は「by value」、「byref」は「by reference」の略です。
サブルーチンの使用を宣言するとき、その中で使う変数の定義も同時に記述しますが、「byval」や「byref」は変数の定義の最初に書きます。
今回のテストプログラムを「byval」を使って書くと下記のようになります。
プログラムファイル sub3a.bas
1 | $regfile = "at26def.dat" | ATtiny26Lを使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | Config Porta = Output | ポートAを出力に設定する。 | |
4 | Dim N As Byte | バイト型変数「N」を使用する。 | |
5 | Declare Sub Routine (byval N As Byte) | サブルーチン「Routine」を使用する。 | |
変数「N」はbyvalに指定。 | |||
6 | For N = 1 to 8 | Forループの範囲ここから。 | |
変数Nに1から8を順に代入する。 | |||
7 | Call Routine ( n ) | サブルーチン「Routine」を呼び出す。 | |
8 | Waitms 500 | 0.5秒間そのまま。 | |
9 | Porta = 0 | ポートAの出力を「0」にする。すべてのLEDが消灯。 | |
10 | Waitms 500 | 0.5秒間そのまま。 | |
11 | Next N | Forループの範囲ここまで。 | |
6行目へ戻り、Nに1を加えて繰り返し。 | |||
12 | Porta = 0 | ポートAを「0」にする。すべてのLEDが消灯する。 | |
13 | End | メインプログラム終わり。 | |
14 | Sub Routine (byval N As Byte) | 「Routine」という名前のサブルーチンここから。 | |
変数「N」はbyvalに指定。 | |||
15 | N = 2 ^ N | 2のN乗を新たなNとする。 | |
16 | Porta = N - 1 | ポートAの出力を「N−1」にする。 | |
17 | End Sub | サブルーチン終わり。呼ばれた場所へ戻る。 |
5行目と14行目では、サブルーチン名のあとに「byval」と書いてから使用する変数名と型を記述します。Forループ1回目、「N=1」のとき、サブルーチン内では「N」が「2」に変わりますが、 「byval」と書いておけば、サブルーチンでの仕事が終わってメインプログラムへ戻るとき、「N」は最初に渡された値、つまり「1」のままで返されます。したがってForループ2回目では「N=2」となり、プログラムは正しく動作します。
「byval」の代わりに「byref」と書くとどうなるかと言うと、何も書かないときと同じです。つまりループ2回目では「N=3」に、3回目では「N=8」になってしまいます。あらためてマニュアルを読んでみると、「byref」がBASCOM-AVRのデフォルト(初期設定)になっているようです。
プログラムの進行にしたがって変数の値がどう変わっていくかを知るには、BASCOM-AVRの「AVR Simulator」が役に立ちます。シミュレーター画面のツールバーの下にある「variable」の欄に「N」と入力してプログラムを1行ずつ実行していくと、「variable 」の右の「value」の欄に、その時点の変数Nの値が表示されます。