アセンブラの設計者が役に立ったと考える範囲で、他の有用な特性を持つ同等の命令に置き換えることができます。
まず、可変長値オペランド フィールドを持つマシンがあります。値/オフセットがいくつかのバリアントのいずれかに適合する場合、アセンブラーが最短のものを置き換えるのが一般的です。(このようなアセンブラでは、特定のサイズを強制できることも一般的です)。これは、即値オペランドとインデックス付きアドレッシングを含む命令に当てはまります。
多くのマシンには、通常は JMP 用、場合によってはロード/ストア/算術命令用の PC 相対オフセットを持つ命令があります。アセンブラは、最初のパスでこのような命令に遭遇すると、アドレス指定されたオペランドが命令の前にあるか、またはまだ命令を認識していないかを判断できます。先行する場合、アセンブラーはオフセットを認識しているため、短い相対形式または長い相対形式を選択できます。次の場合、アセンブラはサイズを認識せず、通常、パス 2 で埋められる命令に大きなオフセットを選択します。同様に、アセンブラに短い形式を選択させる方法もよくあります。
一部のマシンには、ロング ジャンプ相対命令がありません。この場合、ターゲットが jmp の前にあり、近くにある場合、アセンブラーは後方に短い jmp 相対を挿入します。ターゲットが先行しているが遠くにある場合、またはターゲットが前方参照である場合、アセンブラは反対の分岐条件で短い相対 jmp を挿入し、ターゲットが次の命令の後ろにあり、その後に長い絶対 jmp が続く場合があります。(私はこのようなアセンブラを個人的に構築しました)。これにより、jmps は常にターゲットに到達できます。
これらのトリックに関する朗報は、逆アセンブルしても有効なアセンブリ プログラムを取得できることです。
次に、逆アセンブラを混乱させるものに目を向けましょう。
マシンがロード/ストア命令の短い相対アドレス指定を備えていて、プログラマーが明らかに遠く離れた定数または値のロードを指定している場合は、リテラル オペランドに対して相対的にジャンプする同様のトリックを使用できます。この場合、アセンブラは命令を変更して、定数の前後に挿入された短い相対 jmp に続くリテラルまたはアドレス定数を参照します。ディスセンブラーは、命令ストリーム内のすべてが命令であると考えます。この場合、リテラル値はそうではなく、逆アセンブラーがスローされます。少なくとも、逆アセンブラをガイドするリテラルの周りに無条件の jmp があります。
これまでに想像されたすべてのスタントがサポートされている成熟したアセンブラーで見つけることができる、より厄介なトリック。8 ビット アセンブラでの私のお気に入りの 1 つは、「疑似」命令 SKIP1、SKIP2 でした。これは、非常に短い相対ブランチと考えることができます。これらは実際には「CMP #8bits」および「CMP #16bits」命令の opcoode バイトであり、それぞれ 8 ビットまたは 16 ビット命令をジャンプするために使用されていました。したがって、2 バイトではなく「1 バイト」の相対ジャンプです。スペースが不足しているときは、すべてのバイトがカウントされます:-{
SKIP1
INC ; 8 bit instruction
...
これは、ループ エントリで一部のステップを実行してはならないループを実装しようとするときにも便利でしたが、それ以降のループの反復では実行する必要があります。
SKIP2
LOOP: SHLD ; 16 bit instruction
...
BNE LOOP
ここでの問題は、SKIP1 または SKIP2 命令を逆アセンブルすると、INC (または対応する 16 ビット命令) が表示されないことです。
パラメーターを渡すためにアセンブリ言語プログラマーが使用するトリックは、呼び出されたルーチンが戻りアドレスを適切に調整するという条件付きで、呼び出しの後にパラメーターをインラインに配置することです。
CALL foo
DC param1
DC param2
または CALL printstring DC "a variable length string",0
逆アセンブラーがそのような規則が使用されていること、またはその規則が何であるかを知ることができる実際的な方法はないため、逆アセンブラーはこれを間違って処理することになります。