分かりやす〜い
コンピュータ技術情報

TOPに戻る
▼Processor
簡単なアセンブラ言語
┣ 高級、低級の意味と
┃ レジスタ、mov、add

┣ 計算を行う
┣ プログラムを作る
┣ loop命令
┣ Indexレジスタの
┃ 役割とレジスタの
┃ 大きさ
┣ リピートプリフィック
┃ ス・ストリング命令

┣ inc・dec命令
┣ MMX技術
┣ MMXレジスタ
┣ MMX・SIMD・SSE
┣ CMP・JMP命令
┣ 関数とパラメータ
┣ スタック領域
┣ ESPレジスタ
┣ セグメント・
┃   call・ret

┣ コードファイルを作る
┣ コードファイル説明1
┣ コードファイル説明2
┗ マイクロコード

Copyright(C) 2001-2002.ugpop. All rights reserved.




■デジタル用語辞典:
■ セグメント・call・ret ■

前回はスタック領域とその動作について説明しました。

スタック領域というのはメモリ上にあり、「積み重ねるように
して」データを格納する場所の事を言うのでした。
「スタック領域の1番上」の事を「スタックポインタ」と呼び、
「スタックポインタ」は「ESP」レジスタに格納されている事
も説明しました。
また「ESP」レジスタの値(スタックポインタ)は、スタック
領域にデータを格納する命令「push」やスタック領域からデー
タを取り出す命令「pop」を使用する事により、自動的に加減
算される事も説明しました。

「pop」命令と「push」命令の動作イメージをもう1度見てみ
ましょう。


◆pop ebxの動作イメージ

 1.スタック領域からデータを取り出す

             レジスタEBX
            ┌──────┐
   pop ebx┌→│  5   │
          │ └──────┘
          │
 スタック領域│  │   │
       ├──────┤
       │  5   │
       ├──────┤
       │  4   │
       ├──────┤
       │  3   │
       ├──────┤
       │  2   │
       ├──────┤
       │  1   │
       └──────┘


 2.ESP(スタックポインタ)レジスタに4加算

        レジスタESP
       ┌──────┐
       │      │←──「+4」
       └──────┘


「pop ebx」という命令を使用する事により、スタック領域か
らデータを取り出した後に自動的に「ESP」レジスタが4加算
されています。
「EBX」レジスタは4バイトの大きさがあるので4加算される
のでした。

今度は「push」命令の例を見てみましょう。


◆push 5の動作イメージ

 1.ESP(スタックポインタ)レジスタから4減算

        レジスタESP
       ┌──────┐
       │      │←──「−4」
       └──────┘


 2.スタック領域にデータを格納

  push 5─┐
         │
         │
 スタック領域│ ↓    │
       ├──────┤
       │  5   │
       ├──────┤
       │  4   │
       ├──────┤
       │  3   │
       ├──────┤
       │  2   │
       ├──────┤
       │  1   │
       └──────┘


「push」命令ではスタック領域にデータを格納する前に「ESP」
レジスタが減算されています。

これらの動作が行なわれる事により「ESP」レジスタは常にス
タック領域の1番上を示す事が出来るのでした。

前回の説明で関数にパラメータを渡す事が出来るようになった
ので、今回は関数を「呼出す」事について説明したいと思いま
す。

いきなり答えから言うと、インテルプロセッサで関数を呼出す
為には「call」という命令を使用します。

例えば、「sub」という関数を呼出すには

call near ptr sub

と書きます。

間に「near ptr」という記述があります。

call near ptr sub
   ~~~~ ~~~
   ↑
  ニア・ポインタと読みます。

これが付いていると、「subという関数は近くにありますよ。」
という意味になります。

「近くってどこ?」という事になると思うので説明します。

皆さんが使用しているパソコンの中には128メガ、256メ
ガ、512メガ等、環境によりさまざまだと思いますが、必ず
メモリが搭載されているはずです。

このメモリの中で、例えばここからここまではプログラムの実
行コードを格納する場所。ここからここまではデータを格納す
る場所、等と「領域」が決められています。

以下にメモリイメージの例を示します。


           メモリ
     ┌ ┌──────────┐
     │ │          │
     │ │          │
     │ │          │
     │ │          │
     │ ├──────────┤
     │ │プログラム実行コード│
     │ │          │
     │ ├──────────┤
     │ │          │
256メガ│ │          │
     │ │          │
     │ │          │
     │ │          │
     │ ├──────────┤
     │ │          │
     │ │   データ    │
     │ │          │
     │ ├──────────┤
     │ │          │
     │ │          │
     │ │          │
     └ └──────────┘


上の例は全メモリサイズが256メガバイトですが、あくまで
1例です。環境によっては128メガだったり512メガだっ
たりする場合もあると思います。

また、プログラム実行コードが格納されている領域だけを見て
みても、ここからここまではアプリケーションAのプログラム
実行コードを格納する場所。ここからここまではアプリケーシ
ョンBのプログラム実行コードを格納する場所。等と領域が分
けられています。

以下にプログラム実行コード部が更にいくつかの領域に分かれ
ているイメージを示します。


      プログラム実行コード格納領域
       ┌──────────┐
       │          │
       │アプリケーションA │
       │          │
       ├──────────┤
       │          │
       │アプリケーションB │
       │          │
       ├──────────┤
       │          │
       │          │
       │          │
       │          │
       └──────────┘


このようにメモリは複数の「領域」に分けられているのですが、
この「領域」の事を「セグメント」と言います。


インテルプロセッサは「セグメント単位に」保護をかけます。

例えば、アプリケーションAのプログラム実行コードから、ア
プリケーションBの関数を呼出す事は基本的に出来ません。


      プログラム実行コード格納領域
       ┌──────────┐
       │          │
       │アプリケーションA ├──┐
       │          │  │
       ├──────────┤  ×CALL NG
       │          │  │
       │アプリケーションB │←─┘
       │          │
       ├──────────┤
       │          │
       │          │
       │          │
       │          │
       └──────────┘


何故かというと、「セグメント」単位にインテルプロセッサに
より保護がかけられているからです。

同一セグメントの関数を呼出す事はいつでも好きな時に出来ま
す。


      プログラム実行コード格納領域
       ┌──────────┐
       │          ├──┐
       │アプリケーションA │  │CALL OK
       │          │←─┘
       ├──────────┤
       │          │
       │アプリケーションB │
       │          │
       ├──────────┤
       │          │
       │          │
       │          │
       │          │
       └──────────┘

そしてこの「同一セグメント」内の関数呼出しを行う時に「近
くにありますよ。」という意味の「near ptr」という記述をし
てあげます。

call near ptr sub
   ~~~~ ~~~

このようにsubという関数は近く(同一セグメント)にありま
すよ、というのをプロセッサに教えてあげているのです。

少し分かりにくいかもしれませんが、メモリというのは複数の
領域(セグメント)に分けられており、プロセッサはセグメン
ト単位に保護をかけているのだという事だけ覚えておきましょ
う。


以下に「call」命令を使用して関数を呼出しているイメージを
示します。


┌──────────────────────────┐
│      アプリケーションソフトウェア      │
│                          │
└───┬──────────────────────┘
    │ 呼出し
    │call near ptr sub ┌────────┐
    ├────────→│ sub関数  │
    │         │        │
    │         └────────┘


上のイメージでは「sub」という関数を呼出しています。

この「sub」関数ですが、処理が終わった後に元の場所に戻
らなければなりません。

以下にsub関数が処理を終わって、元の場所に戻っているイ
メージを示します。


┌──────────────────────────┐
│      アプリケーションソフトウェア      │
│                          │
└───┬──────────────────────┘
    │ 呼出し
    │call near ptr sub ┌────────┐
    ├────────→│ sub関数  │
    │←──┐     │        │
    │   │     └───┬────┘
    │   │         │
  続きの処理 │      ┌──┴──┐
    ・   │      │ 処理  │
    ・   │      └──┬──┘
    ・   │ 呼出し元へ戻る │
        └─────────┘


上のイメージで、「呼出し元へ戻る」というのがありますが、
これもアセンブラ言語の命令です。

この「呼出し元へ戻る」というのをアセンブラ言語で表すと、
「ret」となります。
「ret」とは、return(戻る)の略です。


ここで皆さん注目して頂きたいのですが、「呼出し元へ戻る」
の「呼出し元」ってどこなのでしょうか?

subという関数は、さまざまな場所から呼出される可能性が
あります。

例えばアプリケーションソフトウェアの1番最初にsub関数
が呼出されたとすると、「ret」命令により、1番最初の方に
処理が戻ります。
また、例えばアプリケーションソフトウェアの1番「最後」に
sub関数が呼出されたとすると、「ret」命令により、1番
「最後」の方に処理が戻ります。

この様に1口に「呼出し元」と言っても、その時々によってさ
まざまな場所を示すのです。


以下にアプリケーションソフトウェアの1番最初の処理でsu
b関数を呼出しているイメージを示します。


┌──────────────────────────┐
│      アプリケーションソフトウェア      │
│                          │
└───┬──────────────────────┘
    │ 呼出し
1番最初│call near ptr sub ┌────────┐
    ├────────→│ sub関数  │
    │←──┐     │        │
    │   │     └───┬────┘
    │   │         │
  続きの処理 │      ┌──┴──┐
    ・   │      │ 処理  │
    ・   │      └──┬──┘
    ・   │ 1番最初に戻る │ret命令
        └─────────┘


「呼出し元」が1番最初の方にあるのでsub関数の処理が
終了したら1番最初に戻っています。
この「1番最初に戻る」というのをアセンブラ言語で書くと

ret

という、たったこれだけの記述になります。


今度は、アプリケーションソフトウェアの1番最後の方でsu
b関数を呼出しているイメージを示します。


┌──────────────────────────┐
│      アプリケーションソフトウェア      │
│                          │
└───┬──────────────────────┘
    │
    │         ┌────────┐
    │      ┌─→│ sub関数  │
    │      │  │        │
    │      │  └───┬────┘
    │      │      │
    │      └──┐┌──┴──┐
    │         ││ 処理  │
    │         │└──┬──┘
    │         │   │ret命令
    │         │   │
    │         │   │
    │         │   │
    │         │   │
    │ 呼出し     │   │
1番最後│call near ptr sub │   │
    ├─────────┘   │
    │←────────────┘
  処理終了    1番最後に戻る


「呼出し元」が1番最後の方にあるのでsub関数の処理が
終了したら1番最後に戻っています。
この「1番最後に戻る」というのをアセンブラ言語で書くと

ret

という、たったこれだけの記述になります。


さて、「1番最初」に戻るのも、「1番最後」に戻るのも、ア
センブラ言語で書くと

ret

という、たったこれだけの記述になってしまいました。

何故、全く同じ命令なのに戻る場所が異なるのでしょうか?


それは、関数を呼出す時にどこに戻れば良いかの情報をスタッ
ク領域に格納しているからです。

誰がスタック領域に格納してくれるのかと言うと、インテルプ
ロセッサが自動的に行なってくれます。

つまり関数を呼出す時には、同時にスタック領域に「どこに戻
れば良いか」の情報も「ハード的に」格納してくれるのです。

以下に「call」命令の動作イメージを示します。
先ほど見て頂いたイメージとは、「call」命令実行時と「ret」
命令実行時の記述にインテルプロセッサ(ハード)が行なう処
理の記述が追加されています。


┌──────────────────────────┐
│      アプリケーションソフトウェア      │
│                          │
└───┬──────────────────────┘
    │ 呼出し                   スタック領域
1番最初│call near ptr sub              ┌─────┐
    ├─────────────────────→│1番最初 │
    │         ┌────────┐   └─────┘
    ├────────→│ sub関数  │
    │←──┐     │        │
    │   │     └───┬────┘
    │   │         │
  続きの処理 │      ┌──┴──┐
    ・   │      │ 処理  │
    ・   │      └──┬──┘
    ・   │         │ret命令     スタック領域
    ・   │         │どこへ戻るか参照┌─────┐
        │ 1番最初に戻る │←───────┤1番最初 │
        └─────────┘        └─────┘


call命令によりsub関数が呼出される前に、スタック領域に
「どこに戻れば良いか」の情報が格納されています。
スタック領域への格納完了後にsub関数に処理が移行されて
います。

「call」というたった1つの命令で、インテルプロセッサはこ
れらの事をハード的に行なってくれるのです。

そしてsub関数の処理が完了したら「ret」命令によりスタ
ック領域が参照されています。
スタック領域から「どこへ戻れば良いか」の情報を参照後に、
呼出し元に処理が移行されています。

「ret」というたった1つの命令で、インテルプロセッサはこ
れらの事をハード的に行なってくれるのです。


今回は関数を呼出す時や元の処理に戻る時に何が行なわれるの
か説明しました。

次回は実際に関数を作ってみて、関数がどのように使われるの
か見てみようと思います。



▲このページの上へ

▲このページの上へ

▲このページの上へ

▲このページの上へ

▲このページの上へ

←前に戻る    ▲このページの上へ    続きを読む→