マイクロプロセッサ8085の命令には、機械制御操作「nop」(操作なし)があります。私の質問は、なぜ手術を必要としないのですか?プログラムを終了する必要がある場合は、HLTまたはRST 3を使用します。または、次の指示に移動する場合は、次の指示を行います。しかし、なぜ操作がないのですか?何が必要ですか?
マイクロプロセッサ8085の命令には、機械制御操作「nop」(操作なし)があります。私の質問は、なぜ手術を必要としないのですか?プログラムを終了する必要がある場合は、HLTまたはRST 3を使用します。または、次の指示に移動する場合は、次の指示を行います。しかし、なぜ操作がないのですか?何が必要ですか?
回答:
CPUおよびMCUでのNOP(またはNOOP、no-operation)命令の1つの使用法は、コードに少しの予測可能な遅延を挿入することです。NOPは何の操作も実行しませんが、それらを処理するには時間がかかります(CPUはオペコードをフェッチしてデコードする必要があるため、少し時間が必要です)。NOP命令を実行するためにわずか1 CPUサイクルが「無駄」になります(通常、正確な数はCPU / MCUデータシートから推測できます)。
ここで、KはNOP命令の処理に必要なサイクル数(ほとんどの場合1)であり、はクロック周期です。
どうしてそうするか?CPUが外部(おそらく遅い)デバイスが作業を完了してCPUにデータを報告するのを少し待つように強制すると便利です。つまり、NOPは同期の目的に役立ちます。
NOPの関連するWikipediaページも参照してください。
別の用途は、のようにも説明し、メモリ内の特定のアドレスに整列コードおよび他の「組立トリック」であるProgrammers.SEに、このスレッドとにStackOverflowの上のこの他のスレッド。
Googleブックページへのこのリンクは、特に8085 CPUを指します。抜粋:
各NOP命令は、フェッチ、デコード、実行に4つのクロックを使用します。
編集 (コメントで表明された懸念に対処するため)
他の答えは、ある時点で実際に実行されるNOPのみを考慮しています。これは非常に一般的に使用されていますが、NOPの唯一の使用ではありません。
非実行NOPは、パッチを適用できるコードを記述するときにも非常に便利です。基本的に、関数にいくつかのNOPを追加します。 後RET
(または同様の命令)。実行可能ファイルにパッチを適用する必要がある場合は、元のコードから開始し、RET
必要な数のNOPを使用して(長いジャンプやインラインコードなど)、別のコードで終了するコードを簡単に追加できますRET
。
このユースケースでは、noöneはNOP
実行を期待します。唯一のポイントは、実行可能ファイルへのパッチ適用を許可することです-理論的にパッドされていない実行可能ファイルでは、関数自体のコードを実際に変更する必要があります(元の境界に適合する場合がありますが、とにかくジャンプが必要になることがよくあります)-それはもっと複雑で、特に手動で書かれたアセンブリや最適化コンパイラを考慮すると; 重要なコードの一部を指し示している可能性のあるジャンプや類似の構造を尊重する必要があります。全体として、かなり注意が必要です。
もちろん、これはこれらの小さなパッチやオンラインのようなパッチを作成するのに便利だった昔はずっと頻繁に使用されていました。今日は、再コンパイルされたバイナリを配布するだけで済みます。パッチを適用するNOPを使用する人がまだいます(実行中かどうか、および必ずしもリテラルではありませんNOP
s-たとえば、WindowsはMOV EDI, EDI
オンラインパッチを適用します-これは、再起動を必要とせずに、システムの実際の実行中にシステムライブラリを更新できる種類です)。
最後の質問は、なぜ実際には何もしないものに専用の指示があるのですか?
MOV AX, AX
はまったく同じに機能しますが、意図を明確に示すものではありません。NOP
非常に多く使用します。実際にコンパイルされたアセンブリコードには、それらはもうありません。NOP
ただし、これらはx86- sではないことに注意してください。NOP
、逆に読み取りやすくなります)とホットパッチ(Visual Studio:Pで編集と続行をはるかに好む)の両方が容易になります。NOPを実行する場合、もちろんさらにいくつかのポイントがあります。
MOV EDI, EDI
、リテラル以外の効果的なNOPがありNOP
ます。MOV EDI, EDI
x86上の2バイトNOPとして最高のパフォーマンスを発揮します。2つNOP
のs を使用した場合、実行する2つの命令になります。編集:
実際、@ DmitryGrigoryevとの議論により、これについてもう少し考えるようになりましたが、この質問/回答への価値のある追加だと思いますので、少し追加してみましょう。
まず、ポイント、明らかに-なぜ次のようなことを行う命令があるのmov ax, ax
でしょうか?たとえば、8086マシンコード(386マシンコードよりも古い)の場合を見てみましょう。
0x90
ます。これは、多くの人々がアセンブリで書いたときでもあります。そのため、専用のNOP
指示がなくても、NOP
キーワード(エイリアス/ニーモニック)は依然として有用であり、それにマップされます。MOV
実際に多くの異なるオペコードにマップするような命令は、時間とスペースを節約します。たとえば、mov al, 42
「即値バイトをal
レジスターに移動する」ことで、0xB02A
(0xB0
オペコード、0x2A
「即時」引数です)。そのため、2バイトかかります。mov al, al
(基本的にそれは愚かなことなので)のショートカットオペコードはないため、mov al, rmb
(rmbが「レジスタまたはメモリ」である)オーバーロードを使用する必要があります。実際には3バイトかかります。(おそらく、mov rb, rmb
2バイトしか必要としない特定性の低いものを代わりにmov al, al
使用しますが、引数バイトはソースレジスタとターゲットレジスタの両方を指定するために使用されます。これで、8086に8つのレジスタしかなかった理由がわかりました:D)。と比較してくださいNOP
、これはシングルバイト命令です!8086のメモリの読み取りは依然として非常に高価であるため、これによりメモリと時間が節約されます。もちろん、そのプログラムをテープやフロッピーなどから読み込むことは言うまでもありません。では、xchg ax, ax
どこから来たのでしょうか?他のxhcg
命令のオペコードを見るだけです。あなたは参照してくださいよ0x86
、0x87
最終的にと0x91
- 0x97
。ですからnop
、これ0x90
はかなり適しているようですxchg ax, ax
(これもxchg
「過負荷」ではありませんxchg rb, rmb
。2バイトでを使用する必要があります)。実際、これは当時のマイクロアーキテクチャの素晴らしい副作用であると確信しています。正しく思い出せば、範囲全体0x90-0x97
を「xchg、レジスタに作用しax
てax
- di
」(オペランドが対称であるため、nop を含む全範囲が得られますxchg ax, ax
。順序はax, cx, dx, bx, sp, bp, si, di
-のbx
後dx
、ax
; レジスタ名はニーモニックであり、順序付けられた名前ではありません-アキュムレータ、カウンタ、データ、ベース、スタックポインタ、ベースポインタ、ソースインデックス、デスティネーションインデックス)。同じアプローチは、mov someRegister, immediate
セットなどの他のオペランドにも使用されました。ある意味では、これはオペコードが実際にはフルバイトではなかったと考えることができます。最後の数ビットは「実」オペランドに対する「引数」です。
これはすべて、x86ではnop
、実際の命令と見なされる場合とそうでない場合があります。元のマイクロアーキテクチャはxchg
、私が正しく思い出した場合のバリアントとしてそれを扱いましたが、実際nop
には仕様で命名されました。そしてxchg ax, ax
、命令としては本当に理にかなっていないので、8086の設計者が、0x90
完全に「おしゃべり」なものに自然にマッピングされるという事実を活用することで、命令デコードでトランジスタとパスウェイを節約する方法を見ることができます。
一方、i8051にはnop
-の完全に設計されたオペコードがあり0x00
ます。ちょっと実用的。命令の設計は、基本的動作のために高いニブルとオペランドを選択するための低ニブルを使用している-例えば、add a
であり0x2Y
、そして0xX8
手段が「0ダイレクト登録」、そう0x28
ですadd a, r0
。シリコンを大幅に節約します:)
CPUの設計(コンパイラーの設計と言語の設計は言うまでもありません)は非常に幅広いトピックであるため、私はまだ先に進むことができましたが、私は多くの異なる視点を示して、それが設計に非常にうまく入っていると思います。
NOP
は、通常はのエイリアスMOV ax, ax
、ADD ax, 0
または同様の指示です。たくさんある場合は何もしない専用の命令を設計するのはなぜですか。
MOV ax, ax
アウェイを最適化する傾向があります。NOP
常に一定のサイクル数で実行されます。しかし、とにかくそれが答えに書いたものにどのように関連しているかはわかりません。
MOV ax, ax
あることを知るまでに、実際にアウェイを最適化することはできませんMOV
。
NOP
and でも同じでしょうMOV ax, ax
)。最近のCPUは、オールドスクールのCコンパイラよりもはるかに複雑です:))
LD r, r
命令がr
ありますが、1つのレジスタはユーザーのMOV ax, ax
命令と同様です。8ではなく7である理由は、命令の1つがにオーバーロードされているためHALT
です。そのため、8080およびZ80には、NOP
!興味深いことに、これらの命令はビットパターンによって論理的に関連していませんが、実行するのに4 Tステートかかるため、LD
命令を使用する理由はありません。
70年代後半に戻って、私(当時私は若い研究生だった)に1024バイトのコード(つまり単一のUVEPROM)で実行される小さな開発システム(メモリが提供されている場合は8080)がありました。 )、保存(S)、印刷(P)、および私が思い出せない何か。実際のテレタイプとパンチテープで駆動されました。しっかりとコーディングされました!
NOOPの使用例の1つは、8バイト間隔で配置された割り込みサービスルーチン(ISR)でした。このルーチンは最終的に9バイト長になり、アドレス空間のわずかに上のアドレスへの(長い)ジャンプで終わりました。これは、リトルエンディアンのバイト順を考えると、上位アドレスバイトが00hであり、次のISRの最初のバイトに挿入されたことを意味します。つまり、(次のISR)はNOOPで始まり、限られたスペースのコード!
したがって、NOOPは便利です。さらに、インテルがそのようにコーディングするのが最も簡単だったと思う-彼らはおそらく実装したい命令のリストを持っていて、すべてのリストがそうであるように「1」から始まった(これはFORTRANの時代だった)ので、ゼロNOOPコードはフォールアウトになりました。(NOOPがコンピューティングサイエンス理論の不可欠な部分であると主張する記事を見たことはありません(数学者はグループ理論のゼロとは異なり、null opを持っていますか?)
0x00
のためにnop
私の答えに。要するに、それは命令のデコードを節約します- xchg ax, ax
命令のデコードが機能する方法から自然に流れ、それは何か「おしゃべり」をしnop
ます。:)命令のデコードのためにシリコン上でかなり節約するために使用されます
一部のアーキテクチャでNOP
は、未使用の遅延スロットを占有するために使用されます。たとえば、分岐命令がパイプラインをクリアしない場合、それ以降のいくつかの命令は実行されます。
JMP .label
MOV R2, 1 ; these instructions start execution before the jump
MOV R2, 2 ; takes place so they still get executed
しかし、その後に適合する有用な指示がない場合はどうなりますJMP
か?その場合、NOP
s を使用する必要があります。
遅延スロットはジャンプに限定されません。一部のアーキテクチャでは、CPUパイプラインのデータの危険は自動的に解決されません。これは、レジスタを変更する各命令の後に、レジスタの新しい値にまだアクセスできないスロットがあることを意味します。次の命令でその値が必要な場合、スロットはaで占有される必要がありますNOP
。
ADD R1, R1
NOP ; The new value of R1 is not ready yet
ADD R1, R3
また、一部の条件付き実行命令(If-True-Falseなど)は各条件にスロットを使用し、特定の条件にアクションが関連付けられていない場合、そのスロットはaで占有される必要がありますNOP
。
CMP R0, R1 ; Compare R0 and R1, setting flags
ITF GT ; If-True-False on GT flag
MOV R3, R2 ; If 'greater than', move R2 to R3
NOP ; Else, do nothing
2バイト NOPの別の使用例:http : //blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx
MOV EDI、EDI命令は2バイトのNOPです。これは、ジャンプ命令にパッチを当てるのに十分なスペースであり、関数をその場で更新できます。その意図は、MOV EDI、EDI命令を2バイトのJMP $ -5命令に置き換えて、関数の開始直前に来る5バイトのパッチスペースに制御をリダイレクトすることです。完全なジャンプ命令には5バイトで十分です。これにより、アドレス空間のどこかにインストールされている置換関数に制御を送信できます。