其中“ +”代表“或”逻辑门,变量的串联表示“与”逻辑门。如何在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
您确定具有四个x_n
值彼此相邻的表达式应该按位与,而不是将它们连接为4位值吗?然后二进制添加?因为我可能已经猜到了。如果是这样,请参见https://codegolf.stackexchange.com/a/203610中的移位和rcl reg, 1
在一对寄存器之间拆分位的方法。
此答案的其余部分是关于优化asm中的操作,该操作执行两组AND(与)操作,或OR(OR)操作一起生成一个布尔值,在AL中产生0
或1
。
您可以对仅提取每个位的简单/简单实现进行一些改进。例如您无需在and之前与AND。第一个AND将使高位全为0,然后不将其设为1,然后第二个AND将其再次设为零。
mov bh, al
; and bh, 01h ; This is pointless
not bh
and bh, 01h ;bh = !x2
您可以更进一步:您仅使用按位运算,仅关心每个寄存器中的低位。 您可以在最后一次and al, 1
隔离所需的位,所有临时人员都在其高位携带垃圾。
要翻转some而不是全部,请使用带有恒定掩码的XOR。例如要翻转AL中的6,4,3,2位并保留其他位不变,请使用xor al, 01011100b
1。然后,您可以转移和移动到单独的寄存器,而无需任何NOT指令。
脚注1:尾随的b
表示以2为底/二进制。如果emu8086支持它,或者如果您必须编写等效的十六进制,则可以在MASM syntax,IDK中使用。
而且您可以与这些寄存器进行AND操作,而不是先提取它们,因此只需要两个临时寄存器。
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注释,我已经忽略了“补码”部分,因为我们在一开始就用一条指令来处理所有这些。)
据我所知,我们采用了一种简单的实现,该实现只是将位移到寄存器的底部,并使用单独的asm指令执行每个布尔操作(除补码之外)。
为了做得更好,我们需要利用我们可以与一条指令并行执行的寄存器中的8(或16)位。由于模式不规则,我们无法轻松地将位打乱以使它们彼此对齐。
IDK,如果有什么聪明的方法,我们可以左移AX以将AL中的位放入AH的底部,以及将AL中的一些分组。嗯,也许将shl ax
与rol al
交替使用以将位发送回AL的底部。但这仍然需要7个移位来分离位。 (对于在一起的连续位(7,6和3,2),shl ax,2
和rol al,2
仅在186上可用,而将计数放在CL中几乎不值得)。
更可能的迎角是FLAGS:大多数ALU操作会根据结果更新FLAGS,如果结果中的所有位均为0,则将ZF设置为1,否则将其设置为1。这使我们对一个寄存器。由于!(a | b)
= !a & !b
,我们可以反转输入中的非互补位,以将其用作水平AND而不是OR。 (我对单个位求反使用!
。在C语言中,!
是一个逻辑非运算符,与~
按位非运算符不同,它会将任何非零数字变为0。)
但是很遗憾,8086没有简单的方法可以直接将ZF转换为寄存器中的0/1。对于CF,这是可能的。我们可以通过使用sub reg, 1
根据寄存器为非零来设置CF设置,如果reg为0(因为借位从顶部开始),则CF会设置CF。否则清除CF。我们可以根据CF用sbb al, al
来获得reg的0 / -1(减去借位)。 al-al部分取消,剩下0 - CF
。
要设置使用标记,我们可以使用AND掩码将位分成两组。
;; 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
根据DL中SBB的结果,我们甚至可以做更多的工作,例如and al, dl
来清除或不清除AL中的位。或者也许用adc al, -1
而不是cmp al, 1
来使用DL的CF结果来影响如何从AL设置CF。
求反/求逆的数量很快就变得很棘手,但是如果每个字节的代码大小或每个性能周期都很重要,那么您就应该研究一下。 (这些日子很少见,在这种情况下,您经常使用SSE2或AVX进行矢量化处理,因此不会有标志,只需在矢量元素内按位并打包比较,就可以将匹配变成全匹配和不匹配放入0。)
请注意,在使用mov / AND分割后,AL和DL都不能为全1,因此加1
永远不会换为零。那么也许sbb al, -1
可以加上0或1并可以设置ZF?
[如果要分支,则在ZF上用jz
或jnz
很好。甚至在8086上可能最好,例如如果第一个AND组给出1
,则不需要隔离另一个组。因此,xor al, ...
相应地补全位,那么test al, mask1
/jnz check_other_group
/ mov al,1
将是快速路径的不错选择。