2017-12-12 3 views
0

私はasm x86でcasetableを理解する上で問題があります。私の教授は、既にこの例を使用したスライドでそれを説明していますx86 asm casetable implementation

.data 
CaseTable BYTE 'A' ; lookup value 
DWORD Process_A ; address of procedure 
EntrySize = ($ - CaseTable) 
BYTE 'B' 
DWORD Process_B 
BYTE 'C' 
DWORD Process_C 
BYTE 'D' 
DWORD Process_D 
NumberOfEntries = ($ - CaseTable)/EntrySize 


mov ebx,OFFSET CaseTable ; point EBX to the table 
mov ecx,NumberOfEntries ; loop counter 

L1: cmp al,[ebx] ; match found? 
jne L2 ; no: continue 
call NEAR PTR [ebx + 1] ; yes: call the procedure 
    ; +1: address after the byte   
jmp Default ; and exit the loop 
L2: add ebx,EntrySize ; point to next entry 
loop L1 ; repeat until ECX = 0 

Default: 

しかし、このコードは不完全であり、私は自分のcaseetableを構築するためにそれを使用する方法がわかりません。誰かが上記のコードを使ってcasetableを実装している実例1つの例を教えてもらえればと思います。私はそれを多く感謝します。この例を使用して、より多くのケースや他のケースを実装する方法を学習します。ありがとうございました。

+1

なぜこのタグは 'java'ですか? – chrylis

+1

教授や先生の助手に相談してください。 –

+3

_ "しかし、このコードは不完全で、私自身のケーステーブルを構築するためにどのように使用するのかわかりません" _ - 教科書やクラスノートに戻り、教授と話をする時間です。私たちはあなたのために仕事をするつもりはありません。 –

答えて

1

あなたはこれを過度に複雑にしています。線形検索やテーブルに格納されたキーは必要ありません。あなたの値をレンジ・チェックし、それをテーブル・インデックスとして使用してください。

私はMASM構文を使用していると思いますので、これをMASM構文で記述しようとしますが、構文が間違っている可能性があります。しかし、実際の命令とロジックは正しいはずです。

section .rdata ; read-only data on Windows 
    CaseTable: 
    DWORD Process_A, Process_B ; function pointers 
    DWORD Process_C, Process_D 
    NumberOfEntries = ($ - CaseTable)/4 
    ; optional: define constants for 'A' and 'D' and use those in the code below 
    ; so the keys/values are still all in one place in the source. 


.text ; or .code or something. 
     ; You were missing a section directive between your data and code. 

; input: selector in EAX 
dispatcher:  ; you were also missing a label for your function 

    ; movzx eax, al ; if your selector really was just a byte 
    sub eax, 'A'   ; convert to idx. values below 'A' wrap to high unsigned 
    cmp eax, 'D' - 'A' ; NumberOfEntries 
    ja @Default   ; unsigned compare rejects out-of-range high or low 
    call [CaseTable + eax*4] 
    ; then fall through. Use jmp as a tail-call if you don't want that. 

@Default: 
    ret 

素敵な(かつ効率的)ASMを書き込むトリックはそれが本当にどのようにシンプルに問題を通じて確認することです。あなたのキーがすべて連続した値であるような特別な状況を手動で利用する必要があります。あなたはコンパイラです。 :)

関数ポインタは

Process_A: 
    mov eax, [esp+4] ; return first arg unchanged 
    ret 

Process_B: 
    mov eax, [esp+4] 
    add eax, eax  ; return n * 2 
    ret 

Process_C: 
    mov eax, [esp+4] 
    lea eax, [eax + eax*2] ; return n * 3 
    ret 

Process_D: 
    mov eax, [esp+4] 
    shl eax, 2  ; return n * 4 
    ret 

もちろん、あなたがちょうど1からの未知の数で乗算するimulを使用したい、このためにディスパッチテーブルを使用することはありませんように、他の機能を指している必要があります4.これは単なる例です。


コンパイラはswitch/case文を最適化するためのクールなトリックをたくさん知っています。ケースラベルの多くは並行して、それらのいずれかの場合をテストするために同じことを、打ち鳴らすとgcc will use an immediate bitmapを行うときに私のお気に入りの一つは次のとおりです。

void errhandler(enum errtype numError) { 
    switch (numError) { 
     case ERROR_01 : // intentional fall-through 
     case ERROR_07 : // intentional fall-through 
     case ERROR_0A : // intentional fall-through 
     case ERROR_10 : // intentional fall-through 
     case ERROR_15 : // intentional fall-through 
     case ERROR_16 : // intentional fall-through 
     //case ERROR_20 : // keep the range of cases smaller for simpler 32-bit code 
     fire_special_event(); 
     break; 

     default: 
     // error codes that require no additional action 
     break;  
    } 
} 

は次のように(clang4.0.1 -O3 -m32 on the Godbolt compiler explorer

errhandler(errtype):     # @errhandler(errtype) 
    mov  eax, dword ptr [esp + 4] # load first function arg 
    cmp  eax, 22 
    ja  .LBB0_2 
    mov  ecx, 6358146 # 0x610482 is a bitmap of those error codes 
    bt  ecx, eax 
    jae  .LBB0_2   # aka JNC: jump if CF=0, i.e. the bit wasn't set, i.e. ((1<<eax) & ecx) was false 
    jmp  fire_special_event() # TAILCALL 
.LBB0_2: 
    ret 
をコードにコンパイル

残念ながら、コンパイラは、条件付きの末尾呼び出しとしてJCCを使用するのに十分にスマートではないので、代わりに彼らは条件付きでjmpを飛び越える:/

gccがの使用を選択しますbtを使用する代わりに、btを使用する代わりに、0/shlを使用することをお勧めします。/