コンセプト
まず、自分が発明者であるかのように全体を考えてみてください。このような:
最初に配列と、それが低レベルでどのように実装されているかを考えてみてください --> 基本的には、連続したメモリ位置 (互いに隣り合っているメモリ位置) のセットです。頭の中にその精神的なイメージができたので、配列のデータを削除または追加するときに、それらのメモリの場所にアクセスして自由に削除できるという事実を考えてみてください。同じ配列について考えてみますが、任意の場所を削除する代わりに、配列内のデータを削除または追加するときに最後の場所のみを削除することにします。その配列のデータをそのように操作するというあなたの新しいアイデアは、後入れ先出しを意味する LIFO と呼ばれます。配列から何かを削除するたびに並べ替えアルゴリズムを使用しなくても、その配列の内容を簡単に追跡できるため、あなたのアイデアは非常に優れています。また、配列内の最後のオブジェクトのアドレスが何であるかを常に知るには、それを追跡するために Cpu 内の 1 つのレジスタを専用にします。さて、レジスターがそれを追跡する方法は、配列から何かを削除または追加するたびに、配列から削除または追加したオブジェクトの量だけレジスターのアドレスの値をデクリメントまたはインクリメントすることです (それらが占有したアドレス空間の量)。また、そのレジスタをデクリメントまたはインクリメントする量がオブジェクトごとに 1 つの量 (4 つのメモリ ロケーション、つまり 4 バイトなど) に固定されていることを確認して、追跡を容易にし、それを可能にすることも必要です。ループは反復ごとに固定のインクリメントを使用するため、そのレジスタをいくつかのループ構成で使用します (例: ループを使用して配列をループするには、反復ごとにレジスタを 4 ずつインクリメントするループを構築します。これは、配列に異なるサイズのオブジェクトが含まれている場合は不可能です)。最後に、この新しいデータ構造を「スタック」と呼ぶことを選択します。これは、レストランのプレートのスタックを思い起こさせ、そのスタックの一番上にあるプレートを常に削除または追加する場合に使用します。
実装
ご覧のとおり、スタックは、操作方法を決定した連続したメモリ位置の配列にすぎません。そのため、スタックを制御するために特別な命令やレジスタを使用する必要さえないことがわかります。次のように、基本的な mov、add、および sub 命令を使用して、ESP と EBP の代わりに汎用レジスタを使用して、自分で実装できます。
移動edx、0FFFFFFFFh
; -->これはコードとデータから最も離れたスタックの開始アドレスになります。また、先ほど説明したスタック内の最後のオブジェクトを追跡するレジスタとしても機能します。これを「スタック ポインター」と呼ぶので、ESP が通常使用されるレジスタ EDX を選択します。
サブEDX、4
mov [edx]、dword ptr [someVar]
; -->これらの 2 つの命令は、スタック ポインターを 4 つのメモリ位置だけデクリメントし、[someVar] メモリ位置から始まる 4 バイトを EDX が現在ポイントしているメモリ位置にコピーします。PUSH 命令が ESP をデクリメントするのと同じように、ここでのみ行いました。手動でEDXを使用しました。したがって、PUSH 命令は基本的に、ESP で実際にこれを行う短いオペコードです。
mov eax、dword ptr [edx]
edxを追加、4
; -->ここでは反対のことを行います。まず、EDX が現在ポイントしているメモリ位置から始まる 4 バイトをレジスタ EAX にコピーします (ここで任意に選択し、必要な場所にコピーすることができました)。次に、スタック ポインター EDX を 4 つのメモリ位置だけインクリメントします。これが POP 命令の機能です。
これで、命令 PUSH と POP およびレジスタ ESP と EBP が、上記の「スタック」データ構造の概念を読み書きしやすくするために Intel によって追加されたばかりであることがわかります。PUSH ans POP 命令とスタック操作用の専用レジスタを持たない RISC (Reduced Instruction Set) Cpu がまだいくつかあり、それらの Cpu のアセンブリ プログラムを作成している間、同じように自分でスタックを実装する必要があります。私はあなたに見せました。