SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine(コードジン) DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

MASMとなでしこによるオリジナルコンパイラの制作

MASMとなでしこによるオリジナルコンパイラの制作 4

メッセージボックス関数の拡張と変数の宣言の実装

メッセージボックスの実装 2

 では、作成したプログラムを実行ファイルにしてみましょう。「build」フォルダの中に新たに「test2.txt」を作って、以下の内容を記述します。

build\test2.txt
msgbox "Hello Word!"
msgbox "Hello Word!", "Title"

 次にコマンドプロントを開き、カレントディレクトリをbuildフォルダに移動してコンパイルします。コンパイルのやり方については前回の記事を参考にしてください。

VEFBLでbuild\test2.txtをコンパイル
..\build test2.txt

 なお、アセンブラはコンパイラと違い、「これをやったら危険だ」というチェック機能はありません。あるとしても実行エラーを返す程度です。そのため、アセンブルする前にソースコードを確認してチェックする習慣を身に付けておいてください。試しに次のコマンドを打ち込んでみます。

ちょっとtest2.txt.asm拝見
type test2.txt.asm
表示
.586
.model flat, stdcall

include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib

.data
STR0 db "Hello Word!", 0
STR1 db "Hello Word!", 0
STR2 db "Title", 0

.code
_start:
invoke MessageBoxA, 0, offset STR0, offset STR0, 0
invoke MessageBoxA, 0, offset STR1, offset STR1, 0
invoke MessageBoxA, 0, offset STR2, offset STR2, 0
invoke ExitProcess, 0
end _start

 「.code」の下で、3回も実行してしまっています。コンパイラのプログラムを確認すると、次の箇所が引っかかります。

26・27行目
違えば、もし、対象が「,」&&文字列中がいいえならば、
  バッファは「{バッファ}{改行}」。

 変数文字列中は、「"」が来た時点でオンとオフが反転し、その時同時にmsgbox命令の分岐も行われてしまっています。

 そこで、これを回避するためアセンブリに置き換えている部分に「対象に改行が入っていた時」を加えます。

build.nakoの条件分岐の部分を移動
もし、コマンドライン¥1が空ならば、
「ファイル名を指定してください」と表示。
終わる。
コマンドライン¥1を開く。
それを表示。

バッファは空。          //変数「空」の中身には何も入っていません
文字列中は、いいえ。    //現在の位置が文字列の中であるか
最新命令は、空。        //さっき指定されたばかりの命令
アセンブリは空。        //出力するアセンブリ
アセンブリデータは空。  //出力するアセンブリデータ
文字列数は0。           //文字列の数

コマンドライン¥1を開く。
それを文字列分解して、反復
        //このようにすれば文字列に変数を埋め込めます
もし、バッファが「print」ならば、
 最新命令はバッファ。
 バッファは空。
         //まだ他の場所にprintがあっても
         //「print(なんとか)print」などとなってしまい
         //検出できなくなる
違えば、もし、バッファが「msgbox」ならば、
 最新命令はバッファ。
 バッファは空。
違えば、もし、対象が「,」&&文字列中がいいえならば、
  バッファは「{バッファ}{改行}」。
違えば、もし、対象が「"」ならば、
            //「"」は一般的な言語では文字列の最初を表す
 もし、文字列中がはいならば、
  文字列中は、いいえ。
 違えば、
  文字列中は、はい。
違えば、もし、対象が改行ならば、
 もし、最新命令が「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。
  バッファは空。
違えば、もし、対象が改行でなければ、
 バッファは「{バッファ}{対象}」。
         //上記以外の場合
         //printの後の半角スペースとかは
         //この行から5行上で対処してしまっています

「.586
.model flat, stdcall

include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib

.data
{アセンブリデータ}

.code
_start:
{アセンブリ}
invoke ExitProcess, 0
end _start
」を「{コマンドライン¥1}.asm」に保存。

 だいぶすっきりしました。printmsgboxの命令のブロックも分かりやすくなったと思います。では、実行ファイルを作成して実行してみます。コマンドプロントのカレントディレクトリを、コンパイルするファイルのあるディレクトリ「build」に移動してください。

コマンドライン
..\build.exe test2.txt
type test2.txt.asm
出力されたアセンブリ
.code
_start:
invoke MessageBoxA, 0, offset STR0, offset STR0, 0
invoke ExitProcess, 0
end _start

 ところが今度は1つしかありません。しかもタイトルにも同じ文字列が表示されるところを見ると、最初の1行しか認識されていないようです。そこで、以下の行に注目します。

34行目
違えば、もし、対象が改行ならば、

 この処理は、「改行のところへ来たら命令の分岐」をするわけです。ところが、プログラムの最後に改行がないため最後の行が認識されないのです。ということで、最後に改行を入れてみました。

build\test2.txt(最後に改行があることに注意)
msgbox "Hello Word!"
msgbox "Hello Word!", "Title"

 これをコンパイルして実行します。

type text2.txt.asmの実行結果(一部)
.data
STR0 db "Hello Word!", 0
STR1 db "Hello Word!", 0
STR2 db "Title", 0

.code
_start:
invoke MessageBoxA, 0, offset STR0, offset STR0, 0
invoke MessageBoxA, 0, offset STR1, offset STR2, 0
invoke ExitProcess, 0
end _start

 今度は成功です。ちなみにSTR0STR1の内容がまったく同じ理由は、違う行で同じ文字列を指定したからです。命令は同じでも行が違うと違う処理になります。では、久しぶりにアセンブルをしてみましょう。なお「astest.exe」で指定するコマンドラインには、最後に「.asm」を入れないようにしてください。自動的に付加されます。

..\astest.exe test2.txt
C:\Documents and Settings\????????\My Documents\samples\codezine\vefbl
\build\test2.txt.asm(16) : error A2006: undefined symbol : MessageBoxA
C:\Documents and Settings\????????\My Documents\samples\codezine\vefbl
\build\test2.txt.asm(17) : error A2006: undefined symbol : MessageBoxA
_
アセンブルエラーが発生しました。

 アセンブルエラーが発生してしまいました。原因を調べてみましょう。C言語やWin32APIの経験のある方には、以下のソースコードがいかに不自然か一目で分かると思います。

.586
.model flat, stdcall

include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib

.data
STR0 db "Hello Word!", 0
STR1 db "Hello Word!", 0
STR2 db "Title", 0

.code
_start:
invoke MessageBoxA, 0, offset STR0, offset STR0, 0
invoke MessageBoxA, 0, offset STR1, offset STR2, 0
invoke ExitProcess, 0
end _start

 上のエラーメッセージを翻訳してみますと「MessageBoxAが不明」という原因でエラーが起きているようです。

 MessageBoxA関数はWin32APIから呼び出されています。Win32APIはDLLによって動作が定義されています。そうです、MessageBoxA関数の定義がされているところは、「user32.dll」というDLLです。ところがアセンブリの中で、「user32.dll」を呼び出しているところがありません。そのため、エラーが起きてしまったのです。

 アセンブリは、テキストで表された機械語であるため、プログラムの動作全てを左右します。そのため、アセンブリを使う方も、プログラムの端から端までの動作について把握しておく必要があります。

 では、呼び出し部分を改めます。

build.nakoの終わりの部分(太字が修正箇所)
「.586
.model flat, stdcall

include iolib.inc
includelib iolib.lib
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib

.data
{アセンブリデータ}

.code
_start:
{アセンブリ}
invoke ExitProcess, 0
end _start
」を「{コマンドライン¥1}.asm」に保存。

 太字の行を追加したら、再度コンパイルします。今度はきちんと動くと思います。ちなみに、入力したコマンドラインの履歴は、F7を押すことで一覧を表示させることができます(Windows 9x系では別途設定が必要)。

 さて、一番大事なプロセスが実行ファイルの起動です。実行ファイルの呼び出しはtest2.txt.exeと入力して行います。

1行目、タイトル指定なし
1行目、タイトル指定なし
2行目、タイトル指定あり
2行目、タイトル指定あり

 見事成功しました。

次のページ
変数

この記事は参考になりましたか?

MASMとなでしこによるオリジナルコンパイラの制作連載記事一覧

もっと読む

この記事の著者

KMY(かみぃ)

とあるごく平凡な中学生。兵庫県在住。日本語プログラミング言語「なでしこ」に惹かれ、たくさんのプログラムを作り利用している。なでしこ以外にも、他の言語を心得ており、HTMLがなでしこの次に得意であり、Perlが第3位(かも)。ベクターにも多彩な作品を残している。作文がど苦手。KMYpage

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/996 2008/08/26 14:03

おすすめ

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー