
■ コードファイル説明2 ■
前回はコードファイルの領域の「宣言」部分を説明しました。
通常、アプリケーションソフトウェアはプログラム実行コード
が格納される領域、初期化済みデータが格納される領域、未初
期化データが格納される領域など、複数の領域に分けられるこ
とを説明しました。
もう1度コードファイルを見てみましょう。
分かりやすいように意味のある部分毎に罫線で囲み、簡単な説
明を付けておきます。
─↓ここから──────────────────────
●領域の「宣言」
┌────────────────────────────┐
│_TEXT segment dword use32 public 'CODE' ;size is 13 │
│_TEXT ends │
│_DATA segment dword use32 public 'DATA' ;size is 0 │
│_DATA ends │
│CONST segment dword use32 public 'CONST' ;size is 0 │
│CONST ends │
│_BSS segment dword use32 public 'BSS' ;size is 0 │
│_BSS ends │
└────────────────────────────┘
●領域を「1つにまとめる」指定
┌────────────────────────────┐
│FLAT group │
└────────────────────────────┘
●「ライブラリ(便利な機能の集まり)」指定
┌────────────────────────────┐
│includelib SNN.lib │
│ extrn __acrtused_con │
└────────────────────────────┘
●「関数」の「宣言」
┌────────────────────────────┐
│ public _main │
│ public _sub │
└────────────────────────────┘
●プログラムコード領域の実体定義
┌────────────────────────────┐
│_TEXT segment │
│ assume CS:_TEXT │
│_main: │
│ push EBX │
│ push ESI │
│ push EDI │
│ call near ptr _sub │
│ pop EDI │
│ pop ESI │
│ pop EBX │
│ ret │
│_sub: │
│ ret │
│_TEXT ends │
└────────────────────────────┘
●その他領域の実体定義
┌────────────────────────────┐
│_DATA segment │
│_DATA ends │
│CONST segment │
│CONST ends │
│_BSS segment │
│_BSS ends │
└────────────────────────────┘
●終了
┌────────────────────────────┐
│ end │
└────────────────────────────┘
─↑ここまで──────────────────────
今回は領域を「1つにまとめる」指定の説明から行ないます。
●領域を「1つにまとめる」指定
┌────────────────────────────┐
│FLAT group │
└────────────────────────────┘
これはWindwosやLinux等を使用している場合に付きます。
「領域を1つにまとめる」とはどういう事でしょうか?
前回、領域を複数に分ける事の意味と必要性について説明しま
した。
折角、領域を複数に分けたのに、今度はそれを1つにまとめて
しまうという指定があるのです。
なんだか矛盾していますよね。
実はWindowsやLinuxなどのOS(オペレーティング・システム)
では、「仮想メモリ」という物が使用されています。
「仮想メモリ」とは、「あたかも大量のメモリが搭載されてい
るかの様に振舞う仕組み」の事です。
例えば、皆さんのパソコンにはメモリが何メガバイト搭載され
ているでしょうか?
大抵、128メガとか、256メガとか、多い方でも512メ
ガバイトだと思います。
例えば「512メガバイト」というのを、16進数という別の
表現方法で表すと「20000000」となります。
しかし実際にパソコンの中で使用可能なアドレスは「00000000」
〜「FFFFFFFF」までです。
「FFFFFFFF」というのは約4ギガバイトです。
つまり、メモリが最大4ギガバイトも搭載されているかのよう
に振舞えるのです。
何故、このような事が出来るのかというと、メモリだけでは足
りない部分はハードディスクを使用しているからです。
つまり、ハードディスクを「メモリの1部分であるかのように」
使用しているのです。
これが「仮想的なメモリ」の概念です。
皆さんの中にも、アプリケーションソフトを大量に動作させた
りなどしている時に、急にパソコンの動きが遅くなってしまっ
たという経験をした方がいらっしゃるのではないでしょうか?
これは「物理的な」メモリが足りなくなってしまい、ハードデ
ィスクへのアクセスが異常に多くなってしまった時に起きる現
象です。
「仮想メモリ」の詳細と動作原理について説明するとそれだけ
で1つの講座が出来上がってしまうので、これ以上の説明は省
略します。(要望が多ければ仮想メモリについての講座を開こ
うと思います。)
ここまで読んで頂いたところで、元に戻ります。
●領域を「1つにまとめる」指定
┌────────────────────────────┐
│FLAT group │
└────────────────────────────┘
「領域を1つにまとめる」とはどういう事でしょうか?
仮想メモリでは、「00000000」〜「FFFFFFFF」までの、1つの
大きな領域しかありません。
ただし、これはあくまでも「仮想的な」領域です。
この仮想的な領域にアクセスするアドレス情報はプロセッサに
より自動的に「物理的な」アドレス情報へと変換されます。
この変換過程において、「仮想的な1つの」領域は「物理的な
複数の」領域に分けられます。
この事をアセンブラ言語ではたった1行で
FLAT group
と書き、複数の領域を1つの大きな領域として扱いますよ、と
いう指定をしてあげているのです。
少し難しくなってしまいましたが、皆さんがプログラムを作成
する時には、仮想メモリについて意識する必要はありません。
全てプロセッサとOSが面倒を見てくれるからです。
普通にメモリが「00000000」〜「FFFFFFFF」までフルに使える
ものとして作成してしまいましょう。
●「ライブラリ(便利な機能の集まり)」指定
┌────────────────────────────┐
│includelib SNN.lib │
│ extrn __acrtused_con │
└────────────────────────────┘
これはDigital Mars社製コンパイラを使用した場合に限って付
いていると思います。
「includelib SNN.lib」というのは簡単に言うと「SNN.libと
いうライブラリを使用しますよ。」という意味になります。
「ライブラリ」というのは「便利な機能の集まり」の事だと思
ってください。
「extrn __acrtused_con」というのは「__acrtused_conとい
う機能を使用しますよ。」という意味になります。
●「関数」の「宣言」
┌────────────────────────────┐
│ public _main │
│ public _sub │
└────────────────────────────┘
これは、「このプログラムには"main"という関数と"sub"とい
う関数がありますよ」という意味になります。
●プログラムコード領域の実体定義
┌────────────────────────────┐
│_TEXT segment │
│ assume CS:_TEXT │
│_main: │
│ push EBX │
│ push ESI │
│ push EDI │
│ call near ptr _sub │
│ pop EDI │
│ pop ESI │
│ pop EBX │
│ ret │
│_sub: │
│ ret │
│_TEXT ends │
└────────────────────────────┘
まず、最初の2行を見てみましょう。
_TEXT segment
assume CS:_TEXT
これは、「ここから"TEXT"セグメント(領域)の実体の開始で
すよ。」という意味です。
「"TEXT"セグメント」というのは前回、領域の「宣言」のとこ
ろで説明しました。
もう1度「"TEXT"セグメント」の領域宣言を見てみましょう。
_TEXT segment dword use32 public 'CODE' ;size is 13
_TEXT ends
この宣言は32ビットモードで動作するプログラムコードが格
納されるという「宣言」でした。
ただの「宣言」なので、「実体」の定義をする必要があるので
すが、その「実体」定義を「_TEXT segment」の部分で行な
います。
_main:
push EBX
push ESI
push EDI
call near ptr _sub
pop EDI
pop ESI
pop EBX
ret
_sub:
ret
これが実際のmain関数とsub関数の定義です。
「call」命令と「ret」命令については既に説明しました。
「call」命令は関数を呼出す命令で、「ret」命令は関数から
復帰する命令でした。(この時、「どこに復帰するか」という
情報を格納する為に、スタック領域が使用されるという事は、
とても重要な事なので忘れないようにしましょう。)
その他の命令として、「push」命令、「pop」命令があります。
これはスタック領域にデータを格納したり、取り出したりする
命令でした。
しかし「push」命令と「pop」命令は「このプログラムでは」
全く無意味な処理です。
何故なら、この命令でスタック領域に格納されている「EBX」
「ESI」「EDI」というレジスタは「1度も使用されていない」
からです。
1度も使用されていないレジスタをわざわざスタック領域に格
納する必要はありません。
ここで示したアセンブリコードは高級言語であるC言語のコン
パイラが出力した物です。
高級言語のコンパイラが出力するコードには、時々、このよう
な無意味なコードが含まれる事があります。
(その為、実行スピードも遅くなります。)
1番最後には「_TEXT ends」というのがあります。
これは説明するまでもありませんが「TEXTセグメント実体の最
後ですよ。」という意味です。
●その他領域の実体定義
┌────────────────────────────┐
│_DATA segment │
│_DATA ends │
│CONST segment │
│CONST ends │
│_BSS segment │
│_BSS ends │
└────────────────────────────┘
それぞれ「データ領域」、「コンスタント領域」、「BSS領域」
の実体の開始と終了です。
このプログラムではそれぞれの領域は使用しませんので開始の
後にすぐ終了しています。
●終了
┌────────────────────────────┐
│ end │
└────────────────────────────┘
このファイルの終了という意味です。
非常に長くなってしまいましたが、これで一通りアセンブラ言
語について見てきた事になります。