私はアセンブリにまったく慣れていません。
次の関数を検討してください。
ここで、「+」は「Or」論理ゲートを表し、変数の連結は「And」論理ゲートを表します。
このような関数をemu8086に実装するにはどうすればよいですか?たとえば、入力パラメータがレジスタALのビットを表す場合、出力はその値を0または1に変更します。
更新:これが私がしたことです、私はそれが悪い書き方であることを知っています、そして何か提案があればそれはうまくいくようです、またはもっと簡単な方法は私
にあなたの助け、特にピーターに感謝します。
org 100h
mov al, 0A3h pour le test
mov ah, al
and ah, 01h ;ah = x0
shr al, 1
mov bl, al
and bl, 01h ;bl = x1
shr al, 1
mov bh, al
and bh, 01h
not bh
and bh, 01h ;bh = !x2
shr al, 1
mov cl, al
and cl, 01h
not cl
and cl, 01h ;cl = !x3
shr al, 1
mov ch, al
and ch, 01h
not ch
and ch, 01h ;ch = !x4
shr al, 1
mov dl, al
and dl, 01h ;x5 = dl
shr al, 1
mov dh, al
and dh, 01h
not dh
and dh, 01h ;dh = !x6
shr al, 1 ;al = x7
and bh, dl
and bh, cl
and bh, ah ;!x2 and x5 and !x3 and x0
and dh, bl
and dh, ch
and dh, al ;!x6 and x1 and !x4 and x7
or dh, bh
mov ah, dh ;resultat dans ah
ret
あなたは必ず4つので表現することをx_n
お互いに次の値をビットごとれ、4ビットの値にそれらを連結していないことになっていますか?そして、バイナリ追加?代わりにそれを推測したかもしれないので。その場合、シフトとレジスタのペア間でビットを分割する方法については、https://codegolf.stackexchange.com/a/203610を参照してくださいrcl reg, 1
。または、BMI2を搭載した最新のx86では、2xpext
を使用add
してそれを行うことができます。
式の各グループのビットが、昇順または降順だけでなく特定の順序であるという事実は、バイトを2つの4ビット整数にアンパックして通常の処理+
を実行するための手がかりとなる可能性があります。
この回答の残りの部分は、2つのグループのANDとORを組み合わせて単一のブール値にし、ALで0
または1
を生成するasmの操作を最適化することについてです。
各ビットを個別に抽出するだけの、単純で単純な実装に加えることができるいくつかの改善があります。例えば、あなたがして、前の必要はありませんし、あなたの後はありません。最初のANDは上位ビットをすべて0のままにし、次にNOTを1にし、2番目のANDを再びゼロにします。
mov bh, al
; and bh, 01h ; This is pointless
not bh
and bh, 01h ;bh = !x2
これをさらに進めることができます。純粋にビット演算を使用しており、各レジスタの下位ビットのみを考慮しています。最後に一度、必要なビットを分離して、すべての一時的なビットでゴミを持ち歩くことができand al, 1
ます。
すべてではなく一部のビットを反転するには、定数マスクでXORを使用します。たとえば、ALのビット6、4、3、2を反転し、他のビットを変更しないままにするには、xor al, 01011100b
1を使用します。次に、NOT命令を必要とせずに、シフトして別のレジスタに移動できます。
脚注1:末尾b
は基数2 /バイナリを示します。これは、emu8086がサポートしている場合、または同等の16進数を記述する必要がある場合は、MASM構文IDKで機能します。
また、最初に抽出する代わりに、これらのレジスタにANDで直接入力できるため、必要なスクラッチレジスタは2つだけです。
xor al, 01011100b ; complement bits 6,4,3,2
mov cl, al ; x0, first bit of the 2&5&3&0 group
shr al, 1
mov dl, al ; x1, first bit of the 6&1&4&7 group
shr al, 1
and cl, al ; AND X2 into the first group, X2 & x0
shr al, 1
and cl, al ; cl = X2 & X3 & x0
... ; cl = 2&5&3&0, dl = 6&1&4 with a few more steps
shr al, 1 ; AL = x7
and al, dl ; AL = x6 & x1 & x4 & x7 (reading 6,1,4 from dl)
or al, cl ; logical + apparently is regular (not exclusive) OR
and al, 1 ; clear high garbage
ret
(プレーンASCIIコメントでは、最初に1つの命令ですべてを処理するため、「補完」部分は無視しました。)
これは、レジスタの最下部にビットを取得し、個別のasm命令を使用して各ブール演算(補数以外)を実行する単純な実装を使用していることを私が理解できる限りです。
より良い結果を得るには、1つの命令と並行して実行できるレジスタの8(または16)ビットを利用する必要があります。パターンが不規則であるため、ビットを簡単にシャッフルして整列させることはできません。
IDKは、AXを左シフトしてALからAHの下部にビットを取得し、ALの上部でビットをグループ化することができる賢い方法がある場合に使用します。うーん、ALの下部にビットを送り返すことshl ax
と交互に行うかもしれませんrol al
。しかし、それでもビットを分離するには7シフトが必要です。(shl ax,2
そしてrol al,2
、一緒に行く連続ビット(7,6と3,2)は186でのみ利用可能であり、CLにカウントを入れることはほとんど価値がありません)。
迎え角の可能性が高いのはFLAGSです。ほとんどのALU操作は結果に応じてFLAGSを更新し、結果のすべてのビットが0の場合はZFを1に設定し、それ以外の場合は1に設定します。これにより、1つのレジスタのビット間で水平OR演算が実行されます。 。以来!(a | b)
= !a & !b
、我々は、水平および代わりのORとしてその使用への入力における非補完ビットを反転させることができます。(私は!
シングルビット反転に使用しています。Cでは、ビット単位のNOT!
とは異なり、ゼロ以外の数値を0に変換する~
論理否定です。)
しかし残念ながら、8086にはレジスタでZFを0/1に直接変換する簡単な方法がありません。(386はsetcc r/m8
、たとえばsetz dl
、ZFに従ってDL = 0または1を設定します。)これはCFで可能です。を使用すると、レジスタがゼロ以外であることに応じてCFを設定できますsub reg, 1
。これは、regが0の場合にCFを設定します(ボローが最上位になるため)。それ以外の場合はCFをクリアします。CFに従ってregで0 / -1を取得できますsbb al, al
(借用で減算)。al-al部分はキャンセルされ、0 - CF
。が残ります。
FLAGSを使用するように設定するには、ANDマスクを使用してビットを2つのグループに分けます。
;; UNTESTED, I might have some logic inverted.
xor al, 10100011b ; all bits are the inverse of their state in the original expression.
mov dl, al
and dl, 11010010b ; ~x[7,6,4,1]
and al, 00101101b ; ~x[5,3,2,0]
cmp dl, 1 ; set CF if that group was all zero (i.e. if the original AND was 1), else clear
sbb dl, dl ; dl = -1 or 0 for the first group
cmp al, 1
sbb al, al ; al = -1 or 0 for the second group. Fun fact: undocumented SALC does this
or al, dl ; The + in the original expression
and al, 1 ; keep only the low bit
ret
and al, dl
DLのSBBの結果によると、ALのビットをクリアするかどうかなど、おそらくさらに多くのことができます。あるいは、DLからのCF結果を使用して、ALからCFを設定する方法に影響を与えるadc al, -1
代わりにcmp al, 1
。
を引く代わりに1
、sub dl, 11010010b
使用したANDマスクを使用して、0
すべてが設定されているかどうかを確認できます。そうでない場合は、ラップしてCFを設定します。それが役立つかどうかはわかりません。
否定/反転の量は頭の中ですぐにやっかいになりますが、コードサイズのすべてのバイトまたはパフォーマンスのすべてのサイクルが重要な場合は、それを調べる必要があります。(最近はめったにありません。SSE2またはAVXを使用してベクトル化することが多いため、フラグはありません。ベクトル要素内でビット単位で比較し、一致をすべて1と非一致に変換します。 0に。)
mov / ANDで分割した後は、ALもDLもオールワンにすること1
はできないため、加算がゼロにラップされることはありません。ではsbb al, -1
、0または1を追加して、ZFを設定できるでしょうか。
分岐する場合は、jz
またはでZFで分岐しても問題ありませんjnz
。これは8086でも最適かもしれません。たとえば、最初のANDグループがを与える場合1
、他のグループを分離する必要はありません。だから、xor al, ...
それに応じてビットを補完するために、その後、test al, mask1
/ jnz check_other_group
/mov al,1
高速パスを通じて良い秋でしょう。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加