2016-12-05 6 views
0

私はスタックにロードされた巨大な数値を持っており、eaxを使ってアクセスしています。 はレジスタに格納できません。私はeaxを使ってそのアドレスを指しています(数字は自然なタイプです。最初の4バイトは符号を含み、次の4は長さ、その他は実際の値を意味します)。膨大な数のシフト - アセンブリ

私はそれをシフトする必要がありますedx回。 LSBからビットを1つずつシフトして(最大8回/バイト)、それらのビットを次のバイトにコピーすることを考えていました。これを行うためには、最初の位置で次のバイトをシフトしなければならず、MSBの位置+1(最悪の場合)まで、またはすべてのシフトが行われ、キャリーフラグが残っていない限り。 P.S.私は明らかにこの特定の状況でshlについて話していたが、ほぼ同じことがshrに当てはまる。

もっと簡単なソリューションはありますか?

+0

'edx'の最大値はいくらですか?左にシフトすると、必要に応じて長さを増やすか、長さを同じにするために結果を切り捨てますか? – user3386109

+0

@ user3386109私は長さを増やす必要があります。 – gigiman

+3

'shld'を使うことができます – harold

答えて

1

古典的な8ビット時代のアイデアを使用することでした(それに非常に同じプロセスを適用した後)、ロールオーバーと次のバイトにそれらを適用するビットを切り取ると思いますRCL(キャリー付きで左に回転する)はDEC counter + JNZでインターリーブされています。なぜなら、x86 DEC/INC命令はゼロフラグにしか影響せず、キャリー(ミステリー解決済み)に影響するのはなぜですか?

ので、コードは、これらの線に沿って行くだろう:

mov edi,address_of_last_byte 
    mov edx,count_of_bytes 
    mov cl,1 
    clc ; clear CF 
loop_1_bit_left: 
    rcl byte [edi],cl ; CF -> LSB, MSB -> CF 
    dec edi ; preserves CF! Goes from last byte to first one 
    dec edx ; preserves CF! Decrement counter 
    jnz loop_1_bit_left ; till whole buffer is shifted 
    ; CF has last bit, will be thrown away unless you do something about it 

は今、これが望まれるためにたくさんの葉...

をバッファのMSBを保存する方法は?私はまず、シフト後に必要なバッファサイズを計算します(new_length = arg_length +(shift + 7)/ 8))。入力をコピーし、MSBの切り捨てに伴う問題を解決するarg_lengthバイト、new_lengthバイトをシフトしないでください。

しかし、別の問題、パフォーマンスがあります。現代のx86 CPUのrclは残念ながら遅いので、このように315ビット分シフトするなどは非常に悪い考えです。しかし、あなたはする必要はありません。すでに入力された数字をnew_lengthバッファに39バイトだけ(最初に向かって)コピーするだけで、最初に312ビットだけシフトできます。そして、残りの3ビットは上記のループによって1つずつシフトします。

さらに出力バッファを十分にパッドする場合は、dword/qword rclバリアント(32b/64bコード)を使用して、同時により多くのバイトを処理できます。 (実際にあなたの説明から、出力バッファを割り当てる責任を負うのは誰か分かりませんが、もしあなたのコードが何らかの形でスタックに戻ってくるのであれば、私はABIがシフト量に従って動的に成長したバッファで可能ですまたはヒープに割り当て、上に数バイトを追加することで、最後の通常のバイトの後に数バイトを変更できます。代わりにdword/qwordと4/8Bアライメント(!)アドレス以上で作業できます。


EDIT:rcl/rcrword/dword参照する変異体は、配列全体に大きな数は、x86のリトルエンディアンの方法を以下であり、ループは正しい++ /を、次のされている場合にのみ正しく動作します - ビットb0-7はバイト配列のオフセット+0にあり、ビットb80~b87は+10オフセットにあり、右へのシフトはMSB(+10)b87からLSB(+0)b0へ向かいます。私の最初のbyte [edi]の例では、MSBがオフセット+0で始まりLSBが+で終わるので、ビッグエンディアンであると予想されているので、ビットは人間の順序で見ることができます。b0、リトルエンディアンは視覚的にバイトグループ(b7 .. b0 b15 .. b8 ... ... ... b87 ... b80)ごとに視覚的に「逆転」しています...少なくとも私はそう思います、とても混乱し始めました。コードを片方向に書くだけで、簡単なコーナーケースの単体テストを作成し、結果を検証し、期待通りの結果を出すことができます。 :D


はちょうどそれはCFの内容を破壊してしまうようあなたは、このような場合のsub edi,4sub rdi,8)でediを更新しないことを確認し、その代わりに、アドレッシングモードによって行わ簡単な計算のlea edi[edi-4]方法を利用します。そして、正確に/4 || /8の値を持つようにカウンタを調整してください。

最高のパフォーマンスを得るには、1つのビットで1から7ビットシフトする価値があります.2ビットから7ビットへのシフトのためには、rclバージョンをそのまま使用してください。 16bのバッファの読取り/書込みを処理し、シフトアウトされたビットを上半分に保持するために、例えば32bレジスタを使用して、目標値を1回で実行する。または、これまでのところでは、shl/and/orの1ビット版がプロファイルされている可能性があります。rclより速くはありません。コンパイラによってrclが使用されていないため、特定のCPUは、単一のrclよりもいくつかのshl/and/or命令を好むかもしれません。


楽しい事実:私は一人で完全に書いた私の非常に最初のZ80アセンブリコードがこれをやっていた、左メモリ1ビットの一つの巨大なエリアをシフト(および右)。その巨大なメモリ領域は実際にはZX SpectrumコンピュータのビデオRAMだったので、1ピクセル(ZXは1ビット/ピクセル)で画像を左右に効果的に動かしていました。

そして私はCFを1回転から他の回転に使用することはできないことを認識していたので、別にビットをマスクして別のレジスタにコピーし、そこから新しいバイトなどに戻します。

私はそれを書き、実行して(バグのためにZXをリセットしました)、バグを修正して実行し、画像がどのように動いているのを見ました... 10倍遅く(毎秒約3フレーム) 「全能高速アセンブリコード」から期待される。その後、私の友人が私にそれを回転させる方法を教えてくれました。コードを20FPSのどこかに走らせました(それでも、私は「高速アセンブリ」でさえ無制限ではなく、私のコードをZXの画面上で見栄えの良いものを入手する)。

+0

そして、私は 'shld'(Haroldのコメント)を忘れていました.2-7ビットの変種シフトコードを作るのは簡単でした。 – Ped7g

+1

'rcl'はx86 x86以前のCPUでも遅かったです。他のすべてのインストラクションが遅かったので、それはちょっとした*相対的なパフォーマンスでした。 :-)楽しい事実や戦争物語といえば、8088/8086ではシフト命令が非常に遅かったので、任意のビット数だけシフトしていたときには、連続して複数のシフト1命令を使用するほうが速かったのです! –

+1

@CodyGrayうん、Z80では、回転やシフトの変形の間にパフォーマンスの違いはありませんでした。そして再び、彼らはとにかく1ビットを許しました。'shld'のような特殊な4ビット回転命令(' rld/rrd')はありませんでしたが、実際には4ビットは次のシフトの準備ができているので、1ビットのr-cfローテーションと同様に連鎖することができます。私はまだ私がそれを必要としなかったことを後悔する(部分的に私はまだZ80でアクティブだった間、私はそれが存在することを忘れていたため) – Ped7g

0

は、私はむしろROLまたはROR値は、

関連する問題