ホーム > プログラミングメモ > 隠し命令
 

隠し命令

CPUには隠し命令、すなわち実装されているもののドキュメントで触れられていない命令があります。 実際にプログラミングして試してみましょう。 なお、これらの命令を実際のアプリケーションに組み込むのは止めましょう。 ドキュメント化されていない、すなわちCPUメーカーが存在を公認していない命令なので、 将来のCPUで動作しなくなる恐れがあります。

SETALC/SALC

SETALC(もしくはSALC)という命令があります。Set AL on Carry Flagの略で、CF(キャリー・フラグ)のビットを ALレジスタの各ビットにコピーします。

【SETALC/SALC】
ニーモニック:SETALCもしくはSALC オペコード:0xD6 擬似コード: IF (CF==0) THEN AL=0x0 ELSE AL=0xFF END

この命令の存在は286の頃から知られていますが、インテルのドキュメントではいまだに隠し命令扱いです。 AMD64ではオペコードマップにSALCのニーモニックで記述があるので隠し命令ではありませんが、 命令そのものについての記述はありません。(オペコードマップの注釈には「Invalid on 64-bit mode」とある。)

テスト用に以下のようなプログラムを作ります。 なお、32ビットと64ビットでソースコードを共用できるようにし、 命令実行時にCPUのEXCEPTIONが発生しても大丈夫なように構造化例外処理(SEH)内でSETALC/SALCを実行します。

【setalcc.c】
#include <windows.h> #include <stdio.h> SIZE_T SetALCbyZero(void); SIZE_T SetALCbyOne(void); void main(void) { BOOL result; // 構造化例外処理機構(SEH)内でSETALC/SALCを実行する。 __try{ result=((SetALCbyZero()==0x0)&&(SetALCbyOne()==0xFF)); } __except(EXCEPTION_EXECUTE_HANDLER){ printf("×CPUでExceptionが発生してしまいました!¥n"); exit(0); } // 結果を表示する。 if(result) { printf("◎SETALC/SALCは実装されているみたいです。¥n"); } else { printf("×SETALC/SALCは実装されていないみたいです。¥n"); } }

【setalca.asm】
; ; [Custom Build Step for Win32] ; debug: ; ml -c -D_WIN64=0 -Zi "-Fl$(IntDir)\$(InputName).lst" "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)" ; release: ; ml -c -D_WIN64=0 "-Fl$(IntDir)\$(InputName).lst" "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)" ; outputs: ; $(IntDir)\$(InputName).obj ; ; [Custom Build Step for Win64] ; debug: ; ml64 -c -D_WIN64=1 -Zi "-Fl$(IntDir)\$(InputName).lst" "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)" ; release: ; ml64 -c -D_WIN64=1 "-Fl$(IntDir)\$(InputName).lst" "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)" ; outputs: ; $(IntDir)\$(InputName).obj ; IFE _WIN64 ; ---- Win32 ---- .686p .model flat, c .code SetALCbyZero PROC xor EAX, EAX not AL ; AL=0xFF clc ; CF=0 db 0d6h ; SETALC(成功すれば、AL=0x0) ret SetALCbyZero ENDP SetALCbyOne PROC xor EAX, EAX ; AL=0x0 stc ; CF=1 db 0d6h ; SETALC(成功すれば、AL=0xFF) ret SetALCbyOne ENDP ELSE ; ---- Win64 ---- .code SetALCbyZero PROC xor RAX, RAX not AL ; AL=0xFF clc ; CF=0 db 0d6h ; SETALC(成功すれば、AL=0x0) ret SetALCbyZero ENDP SetALCbyOne PROC xor RAX, RAX ; AL=0x0 stc ; CF=1 db 0d6h ; SETALC(成功すれば、AL=0xFF) ret SetALCbyOne ENDP ENDIF end

まず、Pentium4-3.06GHz(Northwood, HT対応)+WindowsXP SP2で試してみました。

【Pentium4(Northwood)で実行 - 32ビット】
D:\>setalcc ◎SETALC/SALCは実装されているみたいです。 D:\>

次に、Athlon64 3000+(NewCastle)+x64版WindowsXPで試してみます。

【Athlon64(NewCastle)で実行 - 32ビット(WOW64上)】
D:\>setalcc ◎SETALC/SALCは実装されているみたいです。 D:\>

【Athlon64(NewCastle)で実行 - 64ビット】
D:\>setalcc ×CPUでExceptionが発生してしまいました! D:\>

32ビットについては、実質使えるみたいです。64-bitについては、AMD64ではダメみたいです。EM64Tではどうなるのでしょうか? EM64TがEnableなPentium4/CeleronD/Xeonで果たしてどうなるか、興味津々です。(たぶんAMD64と同じになるのでしょうが。)

その他の命令

その他の命令についても、ニーモニック、オペコード、概要、AMD64オペコードマップ記載の有無のみ 記しておきます。各命令の詳細は「関連リンク」をご覧ください。

【ICEBP】
ニーモニック:ICEBP オペコード:0xF1 概要:ICE(In-Circuit Emulatorの略、ちなみにこれはインテルの登録商標)用の ブレークポイント命令(INT 01) AMD64オペコードマップ:記載あり(INT1, ICE Bkpt)

【UMOV】
ニーモニック:UMOV オペコード:0x0F 0x10, 0x0F 0x11, 0x0F 0x12, 0x0F 0x13 概要:ICE用の命令(User-MOVe) 存在が確認されているCPU:386/486 AMD64オペコードマップ:記載なし(SSE命令(MOVUPS, MOVLPS, MOVHLPS)で使用)

【LOADALL】
ニーモニック:LOADALL オペコード:0x0F 0x05(286の場合), 0x0F 0x07(386/486の場合) 概要:CPUの全状態をロードする。(レジスタだけでなくディスクリプタキャッシュなども含む) 存在が確認されているCPU:286/386/486 AMD64オペコードマップ:記載なし(SYSCALLとSYSRETで使用)

【CMPXCHG】(オペコードが途中で変更された命令)
ニーモニック:CMPXCHG オペコード:0x0F 0xA6, 0x0F 0xA7(486(A-Step)のみ) 0x0F 0xB0, 0x0F 0xB1(486(B-Step)以降) 概要:オペランドとアキュムレータとのアトミックな比較・交換 AMD64オペコードマップ:0x0F 0xA6, 0x0F 0xA7は記載なし(invalid)

【IBTS】(廃止された命令)
ニーモニック:IBTS オペコード:0x0F 0xA7 概要:ビット・ストリングの挿入(Insert Bit String) 存在が確認されているCPU:386(A-Step, B0-Step)のみ AMD64オペコードマップ:記載なし(invalid)

【XBTS】(廃止された命令)
ニーモニック:XBTS オペコード:0x0F 0xA6 概要:ビット・ストリングの抽出(Extract Bit String) 存在が確認されているCPU:386(A-Step, B0-Step)のみ AMD64オペコードマップ:記載なし(invalid)


関連リンク

http://www.x86.org/secrets/opcodes/

執筆日:2004年12月21日(火)
最終更新日:2004年12月30日(木)

Copyright(C) 毛流麦花
 
64ビットCPU(AMD64+EM64T)でアセンブラ