1

我需要在 8086* 程序集中尽可能短(小于 10 个字节)编写以下 C 代码片段,但我只能设法将其写入 12 个字节。

有任何想法吗?

while (ax) {
    bx--;
    ax &= ax-1;
}
4

1 回答 1

2

您尝试的操作称为人口计数,即计算寄存器中设置的位数。

在较新的 CPU 中实际上只有一条指令。而且由于在 2018 年瞄准原始 8086 确实没有那么有趣。

简单的答案是:

f3 0f b8 c0             popcnt eax,eax
29 c3                   sub    ebx,eax 

6 个字节,如果您愿意允许一个正值输入ebx并且可以假设/确保 ebx 一开始就为零,则可以减少到 4 个字节。

请注意,没有必要使用 16 位寄存器,已经很多年没有了。

如果您希望代码在原始 8086(不支持popcnt)上工作,则必须保留循环。

以下非常简单的代码占用 12 个字节:

85 c0                   test   ax,ax          ;is AX zero?
74 08                   je     <done>         ;yes, bail out
<loop>:
4b                      dec    bx             ;bx--
89 c1                   mov    cx,ax
49                      dec    cx             ;cx = ax - 1
21 c8                   and    ax,cx          ;ax = ax & (ax-1) 
75 f8                   jne    <loop>         ;repeat until done
<done>:

您可以通过以稍微低效的方式计算位来将其减少到 9 个字节。这里我们简单地测试一下 ax 的所有 16 位。

<loop>:
d1 e0                   shl    ax,1       ;MSb to carry flag (CF)
83 db 00                sbb    bx,0       ;bx=bx-CF
85 c0                   test   ax,ax      ;is AX zero?
75 f9                   jnz    <loop>     ;if not then loop until done

诀窍是了解代码的作用,然后以不同的方式重新表述它。

于 2018-04-07T18:06:03.587 回答