マイクロプロセッサ8085に「nop」つまりNo operation命令が必要なのはなぜですか?


19

マイクロプロセッサ8085の命令には、機械制御操作「nop」(操作なし)があります。私の質問は、なぜ手術を必要としないのですか?プログラムを終了する必要がある場合は、HLTまたはRST 3を使用します。または、次の指示に移動する場合は、次の指示を行います。しかし、なぜ操作がないのですか?何が必要ですか?


5
NOPは、プログラムのデバッグと更新に頻繁に使用されます。後日、プログラムにいくつかの行を追加する場合は、単にNOPを上書きできます。それ以外の場合は、行を挿入する必要があり、挿入はプログラム全体をシフトすることを意味します。また、誤った命令(誤った)は、同じ引数に従ってNOPに置き換える(単純に上書きする)ことができます。
プルトニウムの密輸業者

わかった しかし、nopを使用すると、スペースも増えます。私たちの主な目標は、それをほとんど場所を取らないようにすることです。
-Demietra95

*私たちの目標は、問題を小さくすることです。それで問題にもなりませんか?
デミエトラ95

2
それが賢明に使用されるべき理由です。それ以外の場合、プログラム全体が単なるNOPになります。
プルトニウム密輸業者

回答:


34

CPUおよびMCUでのNOP(またはNOOP、no-operation)命令の1つの使用法は、コードに少しの予測可能な遅延を挿入することです。NOPは何の操作も実行しませんが、それらを処理するには時間がかかります(CPUはオペコードをフェッチしてデコードする必要があるため、少し時間が必要です)。NOP命令を実行するためにわずか1 CPUサイクルが「無駄」になります(通常、正確な数はCPU / MCUデータシートから推測できます)。

tdelay=NTclockK

ここで、KはNOP命令の処理に必要なサイクル数(ほとんどの場合1)であり、はクロック周期です。Tclock

どうしてそうするか?CPUが外部(おそらく遅い)デバイスが作業を完了してCPUにデータを報告するのを少し待つように強制すると便利です。つまり、NOPは同期の目的に役立ちます。

NOPの関連するWikipediaページも参照してください。

別の用途は、のようにも説明し、メモリ内の特定のアドレスに整列コードおよび他の「組立トリック」であるProgrammers.SEに、このスレッドとにStackOverflowの上のこの他のスレッド

このテーマに関する別の興味深い記事

Googleブックページへのこのリンクは、特に8085 CPUを指します。抜粋:

各NOP命令は、フェッチ、デコード、実行に4つのクロックを使用します。

編集 (コメントで表明された懸念に対処するため)

π


3
多くのこと(特にuCの外部のICを駆動する出力)は、「Dが安定してからクロックエッジまでの最小時間は100 us」、「IR LEDは1 MHzで点滅する必要がある」などのタイミングの制約を受けます。したがって、多くの場合、(正確な)遅延が必要です。
Wouter van Ooijen

6
NOPは、シリアルプロトコルをビットバンギングするときにタイミングを正しくするのに役立ちます。また、プログラムカウンターが破損した場合(PSUグリッチ、まれなガンマ線イベントによる影響など)のまれな状況では、未使用のコードスペースを埋めてコールドスタートベクトルにジャンプし、それ以外の場合は、コードスペースの空の部分。
-Techydude

6
Atari 2600 Video Computer System(カートリッジに保存されたプログラムを実行する2番目のビデオゲームコンソール)では、プロセッサは各スキャンラインで正確に76サイクルを実行し、多くの操作は、開始後に正確なサイクル数で実行する必要がありますスキャンライン。そのプロセッサでは、文書化された「NOP」命令は2サイクルかかりますが、コードでは通常は使用しない3サイクル命令を使用して、正確なサイクル数まで遅延を埋め込みます。コードをより高速に実行すると、表示が完全に文字化けします。
supercat

1
遅延にNOPを使用することは、I / Oデバイスが連続した操作の間に最小時間を課し、最大時間を課さない場合でも、非リアルタイムシステムでも意味があります。たとえば、多くのコントローラーでは、バイトをSPIポートからシフトアウトするには8 CPUサイクルかかります。メモリからバイトをフェッチしてSPIポートに出力するだけのコードはわずかに速すぎますが、SPIポートが各バイトの準備ができているかどうかをテストするロジックを追加すると、不必要に遅くなります。NOPまたは2を追加すると、コードで最大使用可能速度を達成できる場合があります
...-supercat

1
...割り込みがない場合。割り込みがヒットした場合、NOPは不必要に時間を浪費しますが、1つまたは2つのNOPによって浪費される時間は、割り込みによって不要になったかどうかを判断するのに必要な時間よりも短くなります。
supercat

8

他の答えは、ある時点で実際に実行されるNOPのみを考慮しています。これは非常に一般的に使用されていますが、NOPの唯一の使用ではありません。

非実行NOPは、パッチを適用できるコードを記述するときにも非常に便利です。基本的に、関数にいくつかのNOPを追加します。 RET(または同様の命令)。実行可能ファイルにパッチを適用する必要がある場合は、元のコードから開始し、RET必要な数のNOPを使用して(長いジャンプやインラインコードなど)、別のコードで終了するコードを簡単に追加できますRET

このユースケースでは、noöneはNOP実行を期待します。唯一のポイントは、実行可能ファイルへのパッチ適用を許可することです-理論的にパッドされていない実行可能ファイルでは、関数自体のコードを実際に変更する必要があります(元の境界に適合する場合がありますが、とにかくジャンプが必要になることがよくあります)-それはもっと複雑で、特に手動で書かれたアセンブリや最適化コンパイラを考慮すると; 重要なコードの一部を指し示している可能性のあるジャンプや類似の構造を尊重する必要があります。全体として、かなり注意が必要です。

もちろん、これはこれらの小さなパッチやオンラインのようなパッチを作成するのに便利だった昔はずっと頻繁に使用されていました。今日は、再コンパイルされたバイナリを配布するだけで済みます。パッチを適用するNOPを使用する人がまだいます(実行中かどうか、および必ずしもリテラルではありませんNOP s-たとえば、WindowsはMOV EDI, EDIオンラインパッチを適用します-これは、再起動を必要とせずに、システムの実際の実行中にシステムライブラリを更新できる種類です)。

最後の質問は、なぜ実際には何もしないものに専用の指示があるのですか?

  • これは実際の命令です-アセンブリをデバッグまたはハンドコーディングするときに重要です。のような指示MOV AX, AXはまったく同じに機能しますが、意図を明確に示すものではありません。
  • パディング-アライメントに依存するコードの全体的なパフォーマンスを向上させるためだけに存在する「コード」。実行することを意図したものではありません。一部のデバッガーは、逆アセンブリでパディングNOPを単純に非表示にします。
  • コンパイラを最適化するためにより多くのスペースを提供します-まだ使用されているパターンは、コンパイルの2つのステップを持っていることです。最初のステップはかなり単純で、多くの不必要なアセンブリコードを生成し、2番目のステップはクリーンアップし、アドレス参照を再配線して削除します無関係な指示。これは、JITでコンパイルされた言語でもよく見られます。.NETのILとJVMのバイトコードはどちらもNOP非常に多く使用します。実際にコンパイルされたアセンブリコードには、それらはもうありません。NOPただし、これらはx86- sではないことに注意してください。
  • オンラインデバッグにより、読み取り(事前にゼロ化されたメモリがすべて使用されるのでNOP、逆に読み取りやすくなります)とホットパッチ(Visual Studio:Pで編集と続行をはるかに好む)の両方が容易になります。

NOPを実行する場合、もちろんさらにいくつかのポイントがあります。

  • もちろん、パフォーマンス-これが8085にあった理由ではありませんが、80886でさえパイプライン化された命令の実行が既に行われていたため、「何もしない」ことは少し難しくなります。
  • で見たようにMOV EDI, EDI、リテラル以外の効果的なNOPがありNOPます。MOV EDI, EDIx86上の2バイトNOPとして最高のパフォーマンスを発揮します。2つNOPのs を使用した場合、実行する2つの命令になります。

編集:

実際、@ DmitryGrigoryevとの議論により、これについてもう少し考えるようになりましたが、この質問/回答への価値のある追加だと思いますので、少し追加してみましょう。

まず、ポイント、明らかに-なぜ次のようなことを行う命令があるのmov ax, axでしょうか?たとえば、8086マシンコード(386マシンコードよりも古い)の場合を見てみましょう。

  • opcodeを使用した専用のNOP命令があり0x90ます。これは、多くの人々がアセンブリで書いたときでもあります。そのため、専用のNOP指示がなくても、NOPキーワード(エイリアス/ニーモニック)は依然として有用であり、それにマップされます。
  • MOV実際に多くの異なるオペコードにマップするような命令は、時間とスペースを節約します。たとえば、mov al, 42「即値バイトをalレジスターに移動する」ことで、0xB02A0xB0オペコード、0x2A「即時」引数です)。そのため、2バイトかかります。
  • mov al, al(基本的にそれは愚かなことなので)のショートカットオペコードはないため、mov al, rmb(rmbが「レジスタまたはメモリ」である)オーバーロードを使用する必要があります。実際には3バイトかかります。(おそらく、mov rb, rmb2バイトしか必要としない特定性の低いものを代わりにmov al, al使用しますが、引数バイトはソースレジスタとターゲットレジスタの両方を指定するために使用されます。これで、8086に8つのレジスタしかなかった理由がわかりました:D)。と比較してくださいNOP、これはシングルバイト命令です!8086のメモリの読み取りは依然として非常に高価であるため、これによりメモリと時間が節約されます。もちろん、そのプログラムをテープやフロッピーなどから読み込むことは言うまでもありません。

では、xchg ax, axどこから来たのでしょうか?他のxhcg命令のオペコードを見るだけです。あなたは参照してくださいよ0x860x87最終的にと0x91- 0x97。ですからnop、これ0x90はかなり適しているようですxchg ax, ax(これもxchg「過負荷」ではありませんxchg rb, rmb。2バイトでを使用する必要があります)。実際、これは当時のマイクロアーキテクチャの素晴らしい副作用であると確信しています。正しく思い出せば、範囲全体0x90-0x97を「xchg、レジスタに作用しaxax- di」(オペランドが対称であるため、nop を含む全範囲が得られますxchg ax, ax。順序はax, cx, dx, bx, sp, bp, si, di-のbxdxax; レジスタ名はニーモニックであり、順序付けられた名前ではありません-アキュムレータ、カウンタ、データ、ベース、スタックポインタ、ベースポインタ、ソースインデックス、デスティネーションインデックス)。同じアプローチは、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, axADD ax, 0または同様の指示です。たくさんある場合は何もしない専用の命令を設計するのはなぜですか。
ドミトリーグリゴリエフ

@DmitryGrigoryevこれは実際にCPU言語(まあ、マイクロアーキテクチャ)自体の設計に当てはまります。ほとんどのCPU(およびコンパイラー)は、MOV ax, axアウェイを最適化する傾向があります。NOP常に一定のサイクル数で実行されます。しかし、とにかくそれが答えに書いたものにどのように関連しているかはわかりません。
ルアーン

CPUは、命令がパイプラインに既にMOV ax, axあることを知るまでに、実際にアウェイを最適化することはできませんMOV
ドミトリーグリゴリエフ

@DmitryGrigoryevそれは本当にあなたが話しているCPUの種類に依存します。最新のデスクトップCPUは、命令のパイプライン処理だけでなく、多くの処理を実行します。たとえば、CPUはキャッシュラインなどを無効にする必要がないことを認識しており、実際に何もする必要がないことを認識しています(ハイパースレッディング、さらには一般的な複数のパイプにとっても非常に重要です)。それが分岐予測にも影響を与えたとしても、私は驚かないでしょう(ただし、それはおそらくNOPand でも同じでしょうMOV ax, ax)。最近のCPUは、オールドスクールのCコンパイラよりもはるかに複雑です:))
ルアーン

1
誰もnoöneに最適化するための+1!私たちは皆協力して、このスタイルのスペルに戻るべきだと思います!Z80(および8080)には7つのLD r, r命令がrありますが、1つのレジスタはユーザーのMOV ax, ax命令と同様です。8ではなく7である理由は、命令の1つがにオーバーロードされているためHALTです。そのため、8080およびZ80には、NOP!興味深いことに、これらの命令はビットパターンによって論理的に関連していませんが、実行するのに4 Tステートかかるため、LD命令を使用する理由はありません。
CJデニス

5

70年代後半に戻って、私(当時私は若い研究生だった)に1024バイトのコード(つまり単一のUVEPROM)で実行される小さな開発システム(メモリが提供されている場合は8080)がありました。 )、保存(S)、印刷(P)、および私が思い出せない何か。実際のテレタイプとパンチテープで駆動されました。しっかりとコーディングされました!

NOOPの使用例の1つは、8バイト間隔で配置された割り込みサービスルーチン(ISR)でした。このルーチンは最終的に9バイト長になり、アドレス空間のわずかに上のアドレスへの(長い)ジャンプで終わりました。これは、リトルエンディアンのバイト順を考えると、上位アドレスバイトが00hであり、次のISRの最初のバイトに挿入されたことを意味します。つまり、(次のISR)はNOOPで始まり、限られたスペースのコード!

したがって、NOOPは便利です。さらに、インテルがそのようにコーディングするのが最も簡単だったと思う-彼らはおそらく実装したい命令のリストを持っていて、すべてのリストがそうであるように「1」から始まった(これはFORTRANの時代だった)ので、ゼロNOOPコードはフォールアウトになりました。(NOOPがコンピューティングサイエンス理論の不可欠な部分であると主張する記事を見たことはありません(数学者はグループ理論のゼロとは異なり、null opを持っていますか?)


すべてのCPUがNOPを0x00にエンコードしているわけではありません(8085、8080、および私が最もよく知っているCPUであるZ80はすべてそうです)。ただし、プロセッサを設計していた場合、そこに配置します。他に便利なのは、メモリが通常すべて0x00に初期化されるため、コードとしてこれを実行しても、CPUがゼロ以外のメモリに達するまで何も実行されないことです。
CJデニス

x86 CPUを使用していない、なぜ私が説明してきた@CJDennis 0x00のためにnop私の答えに。要するに、それは命令のデコードを節約します- xchg ax, ax命令のデコードが機能する方法から自然に流れ、それは何か「おしゃべり」をしnopます。:)命令のデコードのためにシリコン上でかなり節約するために使用されます
...-Luaan

5

一部のアーキテクチャでNOPは、未使用の遅延スロットを占有するために使用されます。たとえば、分岐命令がパイプラインをクリアしない場合、それ以降のいくつかの命令は実行されます。

 JMP     .label
 MOV     R2, 1    ; these instructions start execution before the jump
 MOV     R2, 2    ; takes place so they still get executed

しかし、その後に適合する有用な指示がない場合はどうなりますJMPか?その場合、NOPs を使用する必要があります。

遅延スロットはジャンプに限定されません。一部のアーキテクチャでは、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

+1。もちろん、それらは後方互換性を気にしないアーキテクチャでのみ表示される傾向があります-命令パイプラインを導入するときにx86がそのようなことを試みた場合、ほとんどすべての人が単にそれを間違って呼び出します(結局、彼らはCPUをアップグレードし、アプリケーションが動作しなくなった!)。したがって、x86 、このような改善がCPUに追加されたときにアプリケーションが気付かないようにする必要があります-とにかくマルチコアCPUに
到達

2

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バイトで十分です。これにより、アドレス空間のどこかにインストールされている置換関数に制御を送信できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.