土曜日はアセンブリの日〜一日目

さて今日は、ジュディオングプロジェクト2日目。
土曜日の言語は、アセンブリ言語にしてみました。
先ほどまでは、”アセンプラ”と読んでいたのですが、
ふつーに考えてアセンブラはおかしいじゃないかと。
JavaやCなどを勉強しようとするときに、コンパイラ勉強しようとは言わないわけですし。
いや、コンパイラ自体を勉強したいのはおいといてね。

写経本

写経本としては、

独習アセンブラ

独習アセンブラ

でいこうかと。
書名は”アセンブラ”となっていますが、
アセンブリ言語」の意味で「アセンブラ」と使うことがよくあります、とやんわりいいながら、著者さんもその使用はお嫌いなようで、
本の中ではきちんと「アセンブリ言語」と「アセンブラ」は用語を使ってらっしゃるようです。
というか、好き嫌い以前に説明ができなくなってしまいますもんね。
ただ、書名は一般化してるからそれにあわせていただいているようですが、
わたしはそれほど大人ではないので、”アセンブリ”と呼んでいくことにします。

環境

で、大体どうやってアセンブリって動かすんだ?って、いわゆる高級言語しか経験のない私なんかはあたふたしてしまうんですが、
何のことはなく、それこそ「アセンブラ」をインストールして、ソースを「アセンブリ言語」で書き、それをアセンブルして、実行ファイルを作って実行するみたい。あたりまえか。
で、アセンブラのインストールなんかで低級なこと(マシン寄りの意味ね)を期待したりして、妙にどきどきしてたわけですが、
ファイルをダウンロードしてきて、伸張・展開(解凍ね。ちょっと言葉に厳密になろうかと)したら、exeがあるので、それをPATHに追加して終わり。
えー、それだけですか?Perlの方がよほど苦労しましたが。ならばと思い、同アセンブラのソースを取ってきて、cygwinなんかでコンパイルインストールしてみたりする。
が、これも難なく成功(make installで警告がでるものの動作は問題ないようす。)しちょっと拍子抜けしました。
もうちょい詳細な環境は以下のとおり。

1.1 実行されるプログラム

コンピュータは 0 と 1 しか扱えません、というものの、具体的にどう 0 と 1 の状態を保持しているかって、意外と意識しなかったりしますね。
電圧の高低で認識されているを知ったのは結構遅かった記憶があります。
CPUにもよるとは思いますが、閾値になる電圧があって、それ以下だったら0 それ以上だったら、1って判断されているんだっけ?
年内にはCPUも自作したいなー。

で、86CPUにて、

 AHレジスタと呼ぶ場所に、値2を保存する

という命令コードは

 10110100 00000010

となり、16進で表現すると

 B4 02

となると。
いいですねー。他の言語だと、”覚えとけ”的な発言は拒絶反応がでるんですが、アセンブリだとそんなこともできませんね。
だって、それ以上説明しようがないですからね。
AHレジスタってのがどこにあって、どんなものなのかはわかりませんが、後ほどご説明いただけると書いてありますから、そのままおいておいて、
とにかく、ここでは、何かしらの記憶領域であることはわかるので、それはよくて、
そこに、2を保存するというと、最後の”0010”が2だろうから、その前までが、
”保存する”というCPUに対する、CPU固有の命令と、”AHレジスタ”って場所の指定が行われているんだろうな。
いや、”AHレジスタに保存する”って命令の可能性も無きにしも非ずですね。
まぁ、ちょっと気になるのは、なんで2なの?ってことと、”2”をあらわすのが、”0010”なのか”00000010”なのかがわからないですね。どちらでもいけそうですし。
ま、気にせずどんどんいきませう。

で、それをさらにアセンブリで書いた場合は、

 mov   ah, 02

となるわけですか。
なんとなく、”02”とかかれているところから、”00000010”が”2”をあらわしてそうな気がしますね。
その辺もここでちょっと解説してほしい気もしますが、導入なので我慢ですね。

1.2 単純なアセンブリ言語プログラム

お、もう書いちゃうんですか?うれしいですね。当然インタラクティブモードはないですねw。ん?つくれなくもないか?
それはおいておいて、リストの最初の行の”;”以下がファイル名でしょうか。
よく、プログラミングの本なんかで、インタラクティブにコンソールでやるのか、ファイル作らなきゃならんのか、ファイル作るときはファイル名に規則があるのか、
また、実行時には、そのファイルを実行するのか、それとも、何かコマンドの引数にそのファイルを渡すのかなど、
結構その辺がわからなくて、つまずくこと多いですね。単純に私がお馬鹿なだけなんですが。
今回の本は比較的わかりやすかったです。
で、そのファイル名でプログラムを書き始めると、ややっ!
最初の行から、言うこと聞いてくれませんね。
私は、”;”と入力したいだけなのに、妙にインデントされかつ、”;;”と入力されてしまいます。
もしや、と思うと、やっぱり!
すげーよ、Emacs(Meadowだけど)!アセンブリのメジャーモードがあるんじゃん!
純粋にそんなところに感動しちゃいました。
おそらく、コメント行だから、”;”だろーが、”;;”だろーが関係ことを期待しつつ、そのまま写経。

ちゃんと、アセンブルの方法も細かく書いてあり、実行方法もちゃんと書いてある。お馬鹿な私にもわかりやすいです。
それだけに、プログラムファイルの作成のところだけがファイル名なんかが、アセンブルの実行例みないとわかんなかったのが、おしいです。
ま、何秒かしかかかんないんですけどw。
で、実行すると、コンソールに

 1

が表示されました。
おお、僕、アセンブリ書いちゃったよ!それ実行しちゃったよ!しかも、うごいちゃったよ!とかって思わずかんどーw。
で、そこから、先ほどの”AHレジスタに2を保存する”の解説がされるわけですが、見開き1ページそれw。
著者さんは、かなり几帳面なご性格の方のようです。日向さんという方ですか。この先がかなり楽しみな感じです。

ここでは、新しい用語がいくつか出てきますね。
アセンブリは単純に機械語を人間にわかりやすいようにマッピングしただけ(というのもシステムコールなんかがあるといえない気もしますが)
なので、そのことをニモニック(Mnemonic)と呼ぶそうです。僕はニーモニックという伸ばす方がカタカナ的には好きなのでそう書きますw。
いや、勝手に私がそう読んでるわけではなくて、アセンブリやっている友達がそういってたので、
そっちの方が定着しちゃっているだけです。
で、その友達が、アセンブリ最初にやりはじめた、というか、会社でやらされ始めたときに、ニーモニックのことを”変数”といって、
教えてくれてた上司に毎日怒られて、ちょっと鬱になっていたのを思い出します。
ニーモニックも、変数もおなじなんじゃん?なんて、Javaしかやってなかった私は適当なことしかいわず、申し訳なかったです。
こうやって、個人的な趣味でやる分には怒られないんで精神衛生上いいですね。
そんな彼も、いっちょまえのアセンブリエンジニアだそうです。ただ、intel86ではなく、巨象のホスト用ってのが玉に瑕ですが。今度おしえてもらう。

ただ、ここの解説でも、”10110100 00000010”のどこまでが”mov”なのかが良くわかんないですね。
”1011”かな、”10110100”なのかな?それとも”mov ah”なのかな?
でも

”ah”と”02”は”オペランド”という、

と書いてあって、

 ”mov”というのは”命令(インストラクション、オペコード)”

というらしいので、”mov ah”ではなさそうですね。
後々、説明されることを期待して、次に行きましょう。

アセンブリコードは大文字でも、小文字でも良いようですね。
区別しないそうです。

あと、先にちょっとでましたが、システムコールを使うこともできるようです。
システムコールってOSの機能ですよね?OSの機能をつかえることも初めて知りましたが、それってうれしくうれしくない気が。。。
だって、アセンブリってCPUに依存してる反面、そのCPUでは動いてくれることを期待するので、OSはとはない動きをしてほしい気がします。
わざわざ、理解しづらい(といわれる)アセンブリを使う場合って、対象のCPUでは動くことを保障するプログラムを組むときではないのかな?
OSに依存するなら、高級言語つかうよ。っていってしまいそうです。
私の認識が間違えているが多分にしますが、現時点ではそうおもいました。

おお、アセンブリを書く際には、レジスタとメモリをイメージすることが大切とあります。
今まではレジスタを意識することはなかったです。メモリさえ意識しないというのはさておき。
今までだと、スタックとヒープを意識してましたが、メモリとレジスタを意識するかんじで。あ、別に、スタックとヒープがメモリとレジスタの関係が同じとかいう風に考えているわけではなくて、というか、スタックとヒープはめもりだし。
どちらも、二つの記憶域を使うという意味では同じように意識できるかなと。
それぞれ、使い方も、挙動も違うとは思いますし。
そのうち、スタックも、ヒープも、レジスタも、意識できるようになるのかな?
この辺の根本的なところの理解がよわいので、そうなれるとうれしいですね。

さて、今度は、文字列表示のプログラミングですね。
試しに、最初のファイル名のコメントを書かなくても動いたので、やっぱり、コメント以上の意味はないのですね。

まず、

 bits 16

で16ビットプログラムであることを示しているらしいのですが、16ビットプログラムと32ビットプログラムが、何がどう違うか
まだわかんないですね。どこかで教えてもらえることを期待しましょう。
次に、

 org 0x100

で、アセンブル後のコードがメモリにロードされたときに、16進100以降のアドレスに配置するようにというインストラクションらしいです。
100以降のアドレスである必要が何かしらあるんでしょうね。それまでは、OSとかが使うんでしょうか?
ただ、一文字表示のときはこんな指定しませんでしたね。
あと、16ビットの指定もしなかったですね。
疑問を胸に次に行きます。

 mov ah, 9

AHレジスタへの値の格納ですね。一文字表示のときはこれが最初でした。
ただ、値は”02”でしたが。このAHレジスタの値が文字列を表示する際の動きを決定するようです。
次に、

 mov dx, msg

お、ちょっと違いますね。一文字表示のときとは。一文字のときは、DLレジスタに16進で文字セットの値”31h”を渡していました。
今回は”msg”となっています。”シンボル”というみたいですね。
どこかのメモリから一定の領域以降に値を保存して、その先頭のメモリのアドレスに”msg”とラベルをつけることのようです。
当然、文字列なので、一つのメモリ領域には入らず、文字の数だけメモリを使うわけですが、そのはじめのメモリにラベルをつけるということのようです。
高級言語で言う、変数と似た感じですね。
ただ、Javaでいえば、変数には実値かアドレス値しかはずから、変数は、一つのメモリ領域にしかつかない
つまり、変数とメモリは1対鄯だと思うので、高級言語の配列名のほうが同じ動きなのでしょうか。
なんとなく、理解できるので、次へいきます。

 int 21h

これで文字列出力ですね。ここは、一文字のときとおなじ。
ということは、
AHレジスタの値が”02”のときはDLレジスタの値を表示。
AHレジスタの値が”9”のときはDXレジスタの値を表示。
という動きを”int 21h ”はするようですね。
って、本にも書いてありますねw。
で、プログラム終了が

 mov ah, 4ch
 mov al, 0
 int 21h

で、これは、一文字表示でもおなじで、今のところ覚えておけとのことです。
最後が”int 21h”であるところをみると、”int 21h”はひょっとすると、何かしらを実行するって命令なのかもしれないですね。あ、命令ではなくて、システムコールか。どうちがうかよくわかってませんが。
で、最後に

 msg db "Hello, assembler$"

というのがきます。LLのようにインタプリティングしながら実行されるわけではないので、
ソース上ではインストラクションより後にデータの保存が書いてあってもへっちゃらです。アセンブルしてできた実行ファイルでは、ちゃんと先に値が保存されるようなプログラムになっているはずです。たぶん。

で、同じプログラムの別の書き方ものっていますが、
同じように実行されることを確認したので、次にいっちゃいます。

1.3 アセンブリ言語プログラミング

次は、足し算のプログラムですね。
加算の演算はなんとなくわかるんですが、
その演算結果を文字列として表示するために、

 add dl,30h

と行っている部分がわかんないですね。なぜ”0”に該当する文字コードを加算すると、そうなるのかな。まぁ、いいや。
それよりも、

 アセンブリ言語プログラムでもいわゆる変数を使うことはできますが、
 可能であれば変数を使わずに、メモリやCPUの特定の場所に値を直接保存したり
 移動したりするのが普通です。

というのが、いまいちわかってない気がしますね。
今まで写経したアセンブリプログラムがレジスタを使ってたのはわかるんですが、
”Hello, assembler”なんかは、メモリ上に確保したのかな?シンボルと変数のちがいは?その辺に注意を払いつつ、進みたいと思います。

あと、呼び方の問題みたいですが

 mov    ah,  02

などを

 (インストラクション)    (ディスティネーションオペランド)(ソースオペランド

というらしいですが、movの場合、宛先と、その元っていみで、上記の呼び方で違和感はないですが、
宛先がないようなインストラクションなども当然あるようで、
第一オペランド、第二オペランドって呼び方もあるらしいです。
オペランドが無いインストラクションもあるらしいです。まぁ、ありえますよね。

それから、そのプログラム行にラベルを貼り付けられるようです。
さっきのシンボルのことですね。
高級言語でよく槍玉にあげられる、goto文なんかでつかわれる、飛び先のラベルと同じでしょうね。
ラベルの書き方は、

 message: db 'Hello, assembler', 13, 10, '$'

でも、

 message db 'Hello, assembler', 13, 10, '$'

と、”:”があっても、無くてもよいようですが、
その”:”のあとの場所に(”:”を使わないときもそこみたい)に、プリフィックスというものをとると、
アドレスサイズが何ビットであるかしていできるみたいです。NASMの場合。
後々学習するようですが、
プログラムが16ビットか32ビットかっていうのは、アドレスサイズの大きさのことをいっているのかしらん?などと考えてしまいました。
メモリと、CPUのフロントバスの本数とは関係ないの?とかちょっと混乱をきたしてみたり。まぁ、追々ってことで。

1.4 アセンブラアセンブリ言語

単純にアセンブリ言語とは機械語に一対一で対応付けられたものだと思ってたのですが、
アセンブルの段階でもうちょっと気の利いたことをやってくれるようです。

  • サブルーチン呼び出し
  • マクロ
  • ループの展開
  • 他のソースコードの読み込み

正確には、プリプロセッサの役目なのかなー。とか思ってみたり。
でも、ここまでやってくれれば、もう普通の高級言語と大して変わらん気もするけど、気のせいでしょうか。

ただ、他のソースコードを読み込むのではなく、
オブジェクトファイルを読み込んで、実行ファイルを作成するのは、アセンブラではなくリンカのお仕事のようです。
その辺も理解が甘くて、勉強しないといけない部分ですが。

2.1 値の表し方

2進数、8進数、10進数、16進数の説明。
この辺は、細かく追うことはしませんが、次の一文に惹かれました。

 実際には、10進数とは異なる数として、2進数とか、8進数とか、16進数という数があるわけではありません。
 2進数も、10進数も、16進数も、同じ値を表現する方法が違うだけです。
 そのため、正しくは「2進法で表現した数」とか「16進法で表現した数」と呼ぶべきです。

すばらしい!高校のときにこの方に数学習いたかったなと思いますね。