nullの取り扱い
ところで、こんなことをやってみます。
Dim tester As Long tester = 45 print "$tester|が入っているんです。"
これをちょっとアセンブルしてみます。
C:\Documents and Settings\????????\My Documents\samples\codezine\vefbl\build\tes t4.txt.asm(13) : error A2047: empty (null) string
エラーが発生してしまいました。エラーメッセージを見てみると、empty (null) string、つまり変数に空っぽの文字列を代入しようとしたことが原因のようです。アセンブリを見てみると、以下のようになっています。
STR0 db "", 0
このような行をどうやってなくせばいいのでしょうか。ちょっとプログラムを見直してみます。
違えば、もし、対象が「$」&&文字列中がはいならば、
//$が変数名の最初(Perlを参考にしました)
文字列中は、いいえ。
もし、最新命令が「print」&&バッファ<>空ならば、
アセンブリデータに「STR{文字列数} db "{バッファ}", 0」を配列追加。
アセンブリに「invoke OutputString, offset STR{文字列数}」を配列追加。
文字列数は文字列数+1。
バッファは空。
ここで<>が登場しました。これは、C言語での!=と同じ意味です。この状態でコンパイルすると、アセンブルに成功し、作った実行ファイルも予想通りの動作をしてくれるようになります。バッファが空かどうか確かめているのは、アセンブリデータの行で代入する文字列の内容が、変数バッファの中身と同じだからです。
ところで、変数を文字列の最初に書いたらエラーが起きたという事は、最後に書いてもエラーが起きるということなのでしょうか。試しにやってみます。
Dim tester As Long tester = 45 print "入っていたのは、な、なんと、$tester|"
案の定、同じようなエラーが起きてしまいました。どこを直せばいいのでしょうか。違えば、もし、対象が「|」&&文字列中がいいえならば、では、バッファを空にしているのですが、文字列は除外してますし最新命令の内容も変えていません。しかもこの後に改行があります。ということは、改行分岐の中のprintで表示する箇所ということになり、ここを変える必要がありそうです。
違えば、もし、対象が改行ならば、
バッファを表示。
もし、予約名スィッチがオンならば、
予約名スィッチはオフ。
予約名は「{予約名}{改行}」。
もし、最新命令が「print」&&バッファ<>空ならば、
アセンブリデータに「STR{文字列数} db "{バッファ}", 0」を配列追加。
アセンブリに「invoke OutputString, offset STR{文字列数}」を配列追加。
文字列数は文字列数+1。
違えば、もし、最新命令が「msgbox」ならば、
アセンブリデータに「STR{文字列数} db "{バッファ¥0}", 0」を配列追加。
もし、バッファ¥1が空ならば、
アセンブリに「invoke MessageBoxA, 0, offset STR{文字列数}, offset STR{文字列数}, 0」を配列追加。
違えば、
アセンブリデータに「STR{文字列数+1} db "{バッファ¥1}", 0」を配列追加。
アセンブリに「invoke MessageBoxA, 0, offset STR{文字列数}, offset STR{文字列数+1}, 0」を配列追加。
文字列数は文字列数+1。 //文字列を1つ多く使ったので
文字列数は文字列数+1。
違えば、もし、最新命令が「Dim」ならば、
これでさっきのスクリプトをコンパイルすると、正常にアセンブルすることができます。
さて、クイズです。このままのコンパイラで、以下のプログラムを正常にアセンブルできるでしょうか?
Dim tester As Long Dim sononi As Long tester = 45 sononi = 73 print "testerには$tester|が、sononiには$sononi|が入っていました。ではtesterに入っているのは?:答え:$tester|"
さっきまでは1つのprintで変数を1つだけ表示していましたが、今度は1つのprintで一度に2つ以上の変数を表示するというものです。
答えは、「正常にアセンブルできる」です。その理由がここに書いてあります(注:どこも変更していません)。
違えば、もし、対象が「$」&&文字列中がはいならば、 //変数名の最初に$をつける(Perlを参考にしました) 文字列中は、いいえ。 もし、最新命令が「print」&&バッファ<>空ならば、 アセンブリデータに「STR{文字列数} db "{バッファ}", 0」を配列追加。 アセンブリに「invoke OutputString, offset STR{文字列数}」を配列追加。 文字列数は文字列数+1。 バッファは空。
太字のところで同時にprint関数の処理をしてしまっているので、正常にアセンブルすることができるのです。
演算の実装
足し算
ここからは四則演算ができるようにしていきます。第1回でのソースコードから足し算の部分を抜き出してみました。
mov eax, 13
push eax
mov edx, 35
push edx
pop edx
pop eax
add eax, edx
push eax
pop eax
mov kekka, eax
invoke OutputInteger, kekka
足し算をし、最後の行で結果kekkaを出力しています。式について詳しく書くと以下のようになります。
mov eax, (足される数)
push eax
mov edx, (足す数)
push edx
pop edx
pop eax
add eax, edx
push eax
pop eax
mov (代入する変数), eax
invoke OutputInteger, (代入する変数)
それでは置き換えてみます。以下のプログラムをコンパイルできるようにしましょう。
Dim tas1 As Long
Dim tas2 As Long
Dim kekka As Long
tas1 = 4
tas2 = 7
kekka = tas1 + tas2
print "$tas1| + $tas2| = $kekka|"
結果はたいたい予想できると思いますが、次のように出力されるはずです。
4 + 7 = 11
これを実現してみます。
(省略)
バッファは空。 //変数「空」の中身には何も入っていません
予約名は空。 //予約名
予約名スィッチは、オフ。 //現在の位置が予約名の中であるか
文字列中は、いいえ。 //現在の位置が文字列の中であるか
最新命令は、空。 //さっき指定されたばかりの命令
アセンブリは空。 //出力するアセンブリ
アセンブリデータは空。 //出力するアセンブリデータ
文字列数は0。 //文字列の数
変更フラグは0。 //変数へ値を代入中?
足し算フラグは0。 //足し算中
(省略)
違えば、もし、対象が「=」&&文字列中がいいえならば、
バッファは「{バッファ}{改行}」。
変更フラグは、はい。
違えば、もし、対象が「+」&&文字列中がいいえならば、
バッファは「{バッファ}{改行}」。
足し算フラグは、はい。
変更フラグは、いいえ。
違えば、もし、対象が「,」&&文字列中がいいえならば、
バッファは「{バッファ}{改行}」。
(省略)
違えば、もし、対象が改行ならば、
(省略)
違えば、もし、変更フラグがはいならば、
変更フラグは、いいえ。
バッファのタブを空に置き換えて、バッファに代入。
バッファ¥0の「 」を空に置き換えて、変数名前に代入。
バッファ¥1の「 」を空に置き換えて、変数名後に代入。
アセンブリに「mov {変数名前},{変数名後}」を配列追加。
違えば、もし、足し算フラグがはいならば、
足し算フラグは、いいえ。
バッファの「 」を空に置き換えて、バッファに代入。
バッファ¥0の「 」を空に置き換えて、代入変数名に代入。
バッファ¥1の「 」を空に置き換えて、変数名前に代入。
バッファ¥2の「 」を空に置き換えて、変数名後に代入。
アセンブリに「mov eax, {変数名前}
push eax
mov edx, {変数名後}
push edx
pop edx
pop eax
add eax, edx
push eax
pop eax
mov {代入変数名}, eax」を配列追加。
バッファは空。
(省略)
足し算フラグをはいにして変更フラグをいいえにしているのは、改行分岐の時、変更フラグがはいのままだと次のように出力されエラーになってしまうからです。
mov kekka,tas1 + tas2
変更フラグがはいの分岐を先にしているので、このままだとバッファ¥1全体を代入する内容と置き換えてしまいます。そのためいいえにしているのです。これをコンパイルすると予想通りの文字が表示されます。
引き算、掛け算、割り算の一括実装
さて、残りの引き算、掛け算、割り算もできるように実装します。正常に動作すれば予想結果のようになるはずです。
Dim tas1 As Long Dim tas2 As Long Dim kekka As Long tas1 = 8 tas2 = 4 kekka = tas1 + tas2 print "$tas1| + $tas2| = $kekka|" kekka = tas1 - tas2 print " : $tas1| - $tas2| = $kekka|" kekka = tas1 * tas2 print " : $tas1| * $tas2| = $kekka|" kekka = tas1 / tas2 print " : $tas1| / $tas2| = $kekka|"
8 + 4 = 12 : 8 - 4 = 4 : 8 * 4 = 32 : 8 / 4 = 2
実装した「build.nako」はサンプルソースを参考にしてください。
